From: Alexis Laferrière Date: Wed, 10 Sep 2014 18:54:40 +0000 (-0400) Subject: Merge branch 'master' into polymorphic_extern_classes X-Git-Tag: v0.6.9~38^2~1 X-Git-Url: http://nitlanguage.org?hp=855c3d4280ce99e452a69c3fc87de567397858f6 Merge branch 'master' into polymorphic_extern_classes --- diff --git a/.gitignore b/.gitignore index 8ed1a4e..65ac23a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ EIFGENs .nit_compile* .nitunit +.nitpretty *.orig bin/nit* doc/stdlib diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore index 77d37a6..c85e49e 100644 --- a/benchmarks/.gitignore +++ b/benchmarks/.gitignore @@ -1,4 +1,7 @@ *.dat *.gnu *.bin +*.png +*.xml +index.html nitg diff --git a/benchmarks/bench_common.sh b/benchmarks/bench_common.sh index c600732..4ea5ffe 100644 --- a/benchmarks/bench_common.sh +++ b/benchmarks/bench_common.sh @@ -37,9 +37,11 @@ function bench_command() echo "** [$title] $desc **" echo " $ $@" + failed= + # Execute the commands $count times for i in `seq 1 "$count"`; do - /usr/bin/time -f "%U" -o "$timeout" -a "$@" > $outputopts 2>&1 || die "$1: failed" + /usr/bin/time -f "%U" -o "$timeout" -a "$@" > $outputopts 2>&1 || { failed=true; die "$1: failed"; } echo -n "$i. " tail -n 1 "$timeout" done @@ -47,6 +49,13 @@ function bench_command() line=`compute_stats "$timeout"` echo "$line ($res)" echo $line >> "$res" + + test -z "$xml" && return + echo >>"$xml" "" + if test -n "$failed"; then + echo >>"$xml" "" + fi + echo >>"$xml" "" } # Run a simble command witout storing the execution time diff --git a/benchmarks/bench_engines.sh b/benchmarks/bench_engines.sh index abc65e3..385d033 100755 --- a/benchmarks/bench_engines.sh +++ b/benchmarks/bench_engines.sh @@ -26,8 +26,6 @@ source ./bench_plot.sh # Can be overrided with 'the option -n' count=2 -pep8analysis=../../pep8analysis - ### HELPER FUNCTIONS ## function die() @@ -56,19 +54,27 @@ function run_compiler() bench_command "bintrees" "bench_bintree_gen 16" "./bintrees.$title.bin" 16 else run_command "$@" ../src/nitg.nit -o "nitg.$title.bin" - bench_command "nitg-g" "nitg --global --no-cc ../src/nitmetrics.nit" "./nitg.$title.bin" -v --global --no-cc ../src/nitmetrics.nit + bench_command "nitg-g" "nitg --global --no-cc ../src/nitls.nit" "./nitg.$title.bin" -v --global --no-cc ../src/nitls.nit bench_command "nitg-s" "nitg --separate ../src/nitg.nit" "./nitg.$title.bin" -v --no-cc --separate ../src/nitg.nit run_command "$@" ../src/nit.nit -o "nit.$title.bin" - bench_command "nit" "nit ../src/test_parser.nit ../src/rapid_type_analysis.nit" "./nit.$title.bin" -v ../src/test_parser.nit -- -n ../src/rapid_type_analysis.nit + bench_command "nit" "nit ../src/test_parser.nit ../src/nitls.nit" "./nit.$title.bin" -v ../src/test_parser.nit -- -n ../src/nitls.nit + run_command "$@" ../src/nitdoc.nit -o "nitdoc.$title.bin" + rm -r out 2> /dev/null + mkdir out 2> /dev/null + bench_command "nitdoc" "nitdoc ../src/nitls.nit" "./nitdoc.$title.bin" -v ../src/nitls.nit -d out run_command "$@" ../examples/shoot/src/shoot_logic.nit -o "shoot.$title.bin" - bench_command "shoot" "shoot_logic 30" "./shoot.$title.bin" 30 + bench_command "shoot" "shoot_logic 15" "./shoot.$title.bin" 15 run_command "$@" ../tests/bench_bintree_gen.nit -o "bintrees.$title.bin" - bench_command "bintrees" "bench_bintree_gen 18" "./bintrees.$title.bin" 18 - if test -f "$pep8analysis/src/pep8analysis.nit"; then - run_command "$@" "$pep8analysis/src/pep8analysis.nit" -I "$pep8analysis/lib" -o "pep8a.$title.bin" - bench_command "pep8analisis" "bench_bintree_gen 18" "./pep8a.$title.bin" "$pep8analysis/tests/privat/"*.pep - fi + bench_command "bintrees" "bench_bintree_gen 17" "./bintrees.$title.bin" 17 + #run_command "$@" "../contrib/pep8analysis/src/pep8analysis.nit" -o "pep8a.$title.bin" + #bench_command "pep8analisis" "bench_pep8analisis" "./pep8a.$title.bin" "../contrib/pep8analysis/tests/privat/"*.pep + run_command "$@" "../lib/ai/examples/queens.nit" -o "queens.$title.bin" + bench_command "queens" "bench_queens 13" "./queens.$title.bin" 13 + run_command "$@" "../lib/ai/examples/puzzle.nit" -o "puzzle.$title.bin" + bench_command "puzzle" "puzzle 15-hard" "./puzzle.$title.bin" kleg.mondcafjhbi fi + + rm -r *.bin .nit_compile out } ## HANDLE OPTIONS ## @@ -97,6 +103,9 @@ while [ "$stop" = false ]; do esac done +xml="bench_engines.xml" +echo "" > "$xml" + NOTSKIPED="$*" if test -z "$NOTSKIPED"; then @@ -107,8 +116,8 @@ fi ## COMPILE ENGINES -# force to use the last nitg, not the bootstraped one -test -f ./nitg || ../bin/nitg ../src/nitg.nit -v +# get the bootstrapped nitg +cp ../bin/nitg . ## EFFECTIVE BENCHS ## @@ -163,8 +172,8 @@ function bench_nitg-g_options() plot "$name.gnu" } -bench_nitg-g_options "slower" --hardening -bench_nitg-g_options "nocheck" --no-check-covariance --no-check-attr-isset --no-check-assert --no-check-autocast --no-check-other +bench_nitg-g_options "slower" --hardening --no-shortcut-range +bench_nitg-g_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert function bench_nitg-s_options() { @@ -190,9 +199,9 @@ function bench_nitg-s_options() plot "$name.gnu" } -bench_nitg-s_options "slower" --hardening --no-inline-intern --no-union-attribute --no-shortcut-equal --no-shortcut-range "--no-gcc-directive likely" "--no-gcc-directive noreturn" -bench_nitg-s_options "nocheck" --no-check-covariance --no-check-attr-isset --no-check-assert --no-check-autocast --no-check-other -bench_nitg-s_options "faster" --inline-coloring-numbers --inline-some-methods --direct-call-monomorph "--inline-some-methods --direct-call-monomorph" +bench_nitg-s_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern "--no-gcc-directive likely --no-gcc-directive noreturn" +bench_nitg-s_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert +bench_nitg-s_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph "--inline-some-methods --direct-call-monomorph" "" function bench_nitg-e_options() { @@ -218,20 +227,26 @@ function bench_nitg-e_options() plot "$name.gnu" } -bench_nitg-e_options "slower" --hardening --no-inline-intern --no-union-attribute --no-shortcut-equal --no-shortcut-range -bench_nitg-e_options "nocheck" --no-check-covariance --no-check-attr-isset --no-check-assert --no-check-autocast --no-check-other --no-check-erasure-cast -bench_nitg-e_options "faster" --inline-coloring-numbers +bench_nitg-e_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern +bench_nitg-e_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert --no-check-erasure-cast +bench_nitg-e_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph --rta function bench_engines() { name="$FUNCNAME" skip_test "$name" && return - prepare_res "$name-nitg-g.dat" "nitg-g" "nitg with --global" - run_compiler "nitg-g" ./nitg --global prepare_res "$name-nitg-s.dat" "nitg-s" "nitg with --separate" run_compiler "nitg-s" ./nitg --separate prepare_res "$name-nitg-e.dat" "nitg-e" "nitg with --erasure" run_compiler "nitg-e" ./nitg --erasure + prepare_res "$name-nitg-sg.dat" "nitg-sg" "nitg with --separate --semi-global" + run_compiler "nitg-sg" ./nitg --separate --semi-global + prepare_res "$name-nitg-eg.dat" "nitg-eg" "nitg with --erasure --semi-global" + run_compiler "nitg-eg" ./nitg --erasure --semi-global + prepare_res "$name-nitg-egt.dat" "nitg-egt" "nitg with --erasure --semi-global --rta" + run_compiler "nitg-egt" ./nitg --erasure --semi-global --rta + prepare_res "$name-nitg-g.dat" "nitg-g" "nitg with --global" + run_compiler "nitg-g" ./nitg --global plot "$name.gnu" } bench_engines @@ -240,10 +255,12 @@ function bench_nitg-e_gc() { name="$FUNCNAME" skip_test "$name" && return - prepare_res "$name-nitg-e-malloc.dat" "nitg-e-malloc" "nitg with --erasure and malloc" - NIT_GC_OPTION="malloc" run_compiler "nitg-e-malloc" ./nitg --erasure prepare_res "$name-nitg-e.dat" "nitg-e" "nitg with --erasure" run_compiler "nitg-e" ./nitg --erasure + prepare_res "$name-nitg-e-malloc.dat" "nitg-e-malloc" "nitg with --erasure and malloc" + NIT_GC_OPTION="malloc" run_compiler "nitg-e-malloc" ./nitg --erasure + prepare_res "$name-nitg-e-large.dat" "nitg-e-large" "nitg with --erasure and large" + NIT_GC_OPTION="large" run_compiler "nitg-e-large" ./nitg --erasure plot "$name.gnu" } bench_nitg-e_gc @@ -318,6 +335,8 @@ if test -n "$html"; then echo >>"$html" "" fi +echo >>"$xml" "" + if test -n "$died"; then echo "Some commands failed" exit 1 diff --git a/contrib/benitlux/.gitignore b/contrib/benitlux/.gitignore new file mode 100644 index 0000000..e4e2a7d --- /dev/null +++ b/contrib/benitlux/.gitignore @@ -0,0 +1,3 @@ +src/benitlux_serial.nit +*.db +*.email diff --git a/contrib/benitlux/Makefile b/contrib/benitlux/Makefile new file mode 100644 index 0000000..4dcab2d --- /dev/null +++ b/contrib/benitlux/Makefile @@ -0,0 +1,8 @@ +all: server + +server: + mkdir -p bin/ + ../../bin/nitg --dir bin/ src/benitlux_daily.nit src/benitlux_web.nit + +src/benitlux_serial.nit: + ../../bin/nitserial -o $@ src/benitlux_web.nit diff --git a/contrib/benitlux/README.md b/contrib/benitlux/README.md new file mode 100644 index 0000000..db4c127 --- /dev/null +++ b/contrib/benitlux/README.md @@ -0,0 +1,34 @@ +Benitlux is an unofficial mailing list to keep faithful bargoers informed of the current beer offer at the excellent Brasserie Bénélux . + +This project is composed of two softwares: + +* a Web interface to subscribe and unsubscribe, +* and a daily background program which updates the BD and send emails. + +The web interface is currently published at + +# Compile and execute + +Make sure all the required packages are installed. Under Debian or Ubuntu, you can use: `apt-get install libevent-dev libsqlite3-dev libcurl4-gnutls-dev sendmail` + +To compile, run: `make` + +To launch the daily background program, run: `bin/benitlux_daily` (the argument `-e` activates sending emails) + +To launch the Web interface, run: `bin/benitlux_web` + +The Web interface will be accessible at + +# Features and TODO + +- [x] Web page parser +- [x] Daily mailer +- [x] Web interface +- [x] Serialization and deserialization of data classes +- [ ] Android app +- [ ] iOS app +- [ ] Charlevoix location support +- [ ] Customize mails (daily, on change, per locations) +- [ ] Authenticate unsubscribe actions over GET +- [ ] Social network and location updates +- [ ] Event updates diff --git a/contrib/benitlux/src/benitlux_controller.nit b/contrib/benitlux/src/benitlux_controller.nit new file mode 100644 index 0000000..84ae549 --- /dev/null +++ b/contrib/benitlux/src/benitlux_controller.nit @@ -0,0 +1,125 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# Actions for the Web interface of Benitlux +module benitlux_controller + +import nitcorn +import json_serialization + +import benitlux_model +import benitlux_db +import benitlux_view + +abstract class BenitluxAction + super Action + + # Path to the database + var db_path = "benitlux_sherbrooke.db" + + # Path to the storage of the last email sent + var sample_email_path = "benitlux_sherbrooke.email" +end + +# Web interface to subscribe to the mailing list +class BenitluxSubscriptionAction + super BenitluxAction + + redef fun answer(request, turi) + do + var template = new BenitluxDocument + + var sub = request.post_args.keys.has("sub") + var unsub = request.all_args.keys.has("unsub") + + var email = null + if request.all_args.keys.has("email") then email = request.all_args["email"].trim + + if email != null then + if email.is_empty or not email.chars.has('@') or not email.chars.has('.') then + template.message_level = "danger" + template.message_content = "Invalid email." + else if sub and request.post_args.keys.has("email") then + template.message_level = "success" + template.message_content = "Subscription successful!" + + var db = new DB.open(db_path) + db.subscribe email + db.close + else if unsub then + template.message_level = "warning" + template.message_content = "You've been unsubscribed." + + var db = new DB.open(db_path) + db.unsubscribe email + db.close + end + end + + if sample_email_path.file_exists then + var f = new IFStream.open(sample_email_path) + var lines = new Array[String] + for line in f.read_all.split_with("\n") do if not line.is_empty then lines.add line + f.close + template.sample_email_lines = lines + end + + var response = new HttpResponse(200) + response.body = template.write_to_string + return response + end +end + +# RESTful interface to compare beer offer between given dates +# +# Expects request in the format of `since/2014-07-24`, will replay with a +# `BeerEvents` serialized to Json with the necessary meta-data to be deserialized. +class BenitluxRESTAction + super BenitluxAction + + redef fun answer(request, turi) + do + var words = turi.split("/") + if not words.is_empty and words.first.is_empty then words.shift + + if words.length >= 2 and words[0] == "since" then + var since = words[1] + + var db = new DB.open(db_path) + var events = db.beer_events_since(since.to_sql_string) + db.close + + if events == null then + var response = new HttpResponse(400) + response.body = "Bad request" + return response + end + + var stream = new StringOStream + var serializer = new JsonSerializer(stream) + serializer.serialize events + var serialized = stream.to_s + + var response = new HttpResponse(200) + response.body = serialized + return response + end + + var response = new HttpResponse(400) + response.body = "Bad request" + return response + end +end diff --git a/contrib/benitlux/src/benitlux_daily.nit b/contrib/benitlux/src/benitlux_daily.nit new file mode 100644 index 0000000..8121683 --- /dev/null +++ b/contrib/benitlux/src/benitlux_daily.nit @@ -0,0 +1,240 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# Daily program to fetch and parse the Web site, update the database and email subscribers +module benitlux_daily + +import curl +import sendmail +import opts + +import benitlux_model +import benitlux_db + +redef class Text + # Return a `String` without any HTML tags (such as `
`) from `recv` + fun strip_tags: String + do + var str = to_s + var new_str = "" + + var from = 0 + loop + var at = str.index_of_from('<', from) + if at == -1 then break + + new_str += str.substring(from, at-from) + + at = str.index_of_from('>', at) + assert at != -1 + + from = at+1 + end + + return new_str + end + + # Return an `Array` of the non-empty lines in `self` + # + # assert ["a", "asdf", "", " ", " ", "b"].to_clean_lines == ["a", "asdf", "b"] + fun to_clean_lines: Array[String] + do + var orig_lines = split_with("\n") + var new_lines = new Array[String] + + for line in orig_lines do + line = line.trim + + # remove empty lines + if line == " " then continue + if line.is_empty then continue + + new_lines.add line.to_s + end + + return new_lines + end +end + +# Main logic of the program to be executed daily +class Benitlux + # The street on which is the Benelux + var street: String + + # The url of this precise Benelux + var url: String + + # Path to the database + var db_path: String + + # Where to save the sample email + var sample_email_path: String + + init(street: String) + do + self.street = street + self.url = "www.brasseriebenelux.com/{street}" + self.db_path = "benitlux_{street}.db" + self.sample_email_path = "benitlux_{street}.email" + end + + # Execute the main program logic + fun run(send_emails: Bool) + do + # Get the web page + var body = download_html_page + + # Parse the Web page and get the available beers + var beers = parse_beers_from_html(body) + + var db = new DB.open(db_path) + + # Update the database with the beers of the day + db.insert_beers_of_the_day beers + + # Query the beer-related events of today + var beer_events = db.beer_events_today + + # Generate the email title and content, store them in attributes + generate_email(beer_events) + + # Save as sample email to file + var f = new OFStream.open(sample_email_path) + f.write email_title + "\n" + for line in email_content do f.write line + "\n" + f.close + + # Set the email if desired + if send_emails then + var subs = db.subscribers + send_emails_to subs + end + + db.close + end + + # Fetch the Web page at `url` + fun download_html_page: String + do + var curl = new Curl + + var request = new CurlHTTPRequest(url, curl) + var response = request.execute + + if response isa CurlResponseSuccess then + var body = response.body_str + curl.destroy + return body + else if response isa CurlResponseFailed then + print "Failed downloading URL '{url}' with: {response.error_msg} ({response.error_code})" + exit 1 + end + abort + end + + # Extract the beers of the day information from the HTML if `body` + fun parse_beers_from_html(body: String): HashSet[Beer] + do + # Parts of the HTML page expected to encapsulate the interesting section + var header = "

Bières

" + var ender = "" + + var match = body.search(header) + assert match != null else print body + var start = match.after + + match = body.search_from(ender, start) + assert match != null + var finish = match.from + + var of_interest = body.substring(start, finish-start) + var lines = of_interest.strip_tags.to_clean_lines + + var beers = new HashSet[Beer] + for line in lines do + var parts = line.split(" - ") + if parts.length >= 2 then + beers.add new Beer(parts[0].trim, parts[1].trim) + end + end + return beers + end + + # Content lines of the email + var email_content: Array[String] + + # Title of the email + var email_title: String + + # Generate email and fill the attributes `email_content` and `email_title` + fun generate_email(beer_events: BeerEvents) + do + email_title = beer_events.to_email_title + email_content = beer_events.to_email_content + end + + # Send the email to all the addresses in `subs` + fun send_emails_to(subs: Array[String]) + do + for email in subs do + var unsub_link = "http://benitlux.xymus.net/?unsub=&email={email}" + var content = """ +{{{email_content.join("
\n")}}} +

+To unsubscribe, go to {{{unsub_link}}} +""" + + var mail = new Mail("Benitlux ", email_title, content) + mail.to.add email + mail.header["Content-Type"] = "text/html; charset=\"UTF-8\"" + mail.encrypt_with_base64 + + mail.send + end + end +end + +redef class OptionContext + # Shall we mail the mailing list? + var send_emails = new OptionBool("Send emails to subscribers", "-e", "--email") + + # Print the usage message + var help = new OptionBool("Print this help message", "-h", "--help") + + redef init do add_option(send_emails, help) +end + +# Avoid executing when running tests +if "NIT_TESTING".environ == "true" then exit 0 + +var opts = new OptionContext +opts.parse args +if not opts.errors.is_empty or opts.help.value == true then + print opts.errors.join("\n") + print "Usage: benitlux_daily [Options]" + opts.usage + exit 1 +end + +var ben = new Benitlux("sherbrooke") +ben.run(opts.send_emails.value or else false) + +# The parsing logic for the wellington locaiton is active (to gather data) +# but the web interface do not allow to subscribe to its mailing list. +# +# TODO revamp mailing list Web interface +ben = new Benitlux("wellington") +ben.run(opts.send_emails.value or else false) diff --git a/contrib/benitlux/src/benitlux_db.nit b/contrib/benitlux/src/benitlux_db.nit new file mode 100644 index 0000000..05a1a81 --- /dev/null +++ b/contrib/benitlux/src/benitlux_db.nit @@ -0,0 +1,149 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# Database interface to be used by the Web server and daily program +module benitlux_db + +import sqlite3 + +import benitlux_model + +# The database of this project +class DB + super Sqlite3DB + + redef init open(path) + do + super + + create_tables + end + + # Create all tables for this project (IF NOT EXISTS) + fun create_tables + do + assert create_table("IF NOT EXISTS beers (name TEXT PRIMARY KEY, desc TEXT)") else + print error or else "?" + end + + assert create_table("IF NOT EXISTS daily (beer INTEGER, day DATE)") else + print error or else "?" + end + + assert create_table("IF NOT EXISTS subscribers (email TEXT UNIQUE PRIMARY KEY, joined DATETIME DEFAULT CURRENT_TIMESTAMP)") else + print error or else "?" + end + end + + # Update the DB with a all the `beers` available today + # + # Delete any other previous information for today. + fun insert_beers_of_the_day(beers: HashSet[Beer]) + do + # Clean the DB of the previous beers of the day + assert execute("DELETE FROM daily WHERE day == date('now')") else + print error or else "?" + end + + # Add beer info + for beer in beers do + # Add meta if not there + assert execute("INSERT OR IGNORE INTO beers (name, desc) VALUES ({beer.name.to_sql_string}, {beer.desc.to_sql_string})") else + print error or else "?" + end + + # Add day + assert execute("INSERT INTO daily (beer, day) VALUES (" + + "(SELECT min(ROWID) FROM beers WHERE lower(name) = lower({beer.name.to_sql_string})), " + + "date('now'))") else + print error or else "?" + end + end + end + + # Build and return a `BeerEvents` for today compared to the last weekday + fun beer_events_today: BeerEvents + do + var tm = new Tm.localtime + var last_weekday + if tm.wday == 1 then + # We're monday! we compare with the last friday + last_weekday = "date('now', 'weekday 6', '-7 day')" + else last_weekday = "date('now', '-1 day')" + + return beer_events_since(last_weekday).as(not null) # This is used by daily + end + + # Build and return a `BeerEvents` for today compared to `prev_day` + # + # Return `null` on error + fun beer_events_since(prev_day: String): nullable BeerEvents + do + var events = new BeerEvents + + # New + var stmt = select("name, desc FROM beers WHERE " + + "ROWID IN (SELECT beer FROM daily WHERE date(day) = date('now')) AND " + + "NOT ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))") + if stmt == null then return null + for row in stmt do events.new_beers.add row.to_beer + + # Gone + stmt = select("name, desc FROM beers WHERE " + + "NOT ROWID IN (SELECT beer FROM daily WHERE date(day) = date('now')) AND " + + "ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))") + if stmt == null then return null + for row in stmt do events.gone_beers.add row.to_beer + + # Fix + stmt = select("name, desc FROM beers WHERE " + + "ROWID IN (SELECT beer FROM daily WHERE date(day) = date('now')) AND " + + "ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))") + if stmt == null then return null + for row in stmt do events.fix_beers.add row.to_beer + + return events + end + + # All the subscribers to the mailing list + fun subscribers: Array[String] + do + var subs = new Array[String] + for row in select("email FROM subscribers") do subs.add row[0].to_s + return subs + end + + # Add `email` to the mailing list subscribers + fun subscribe(email: String) + do + assert insert("OR IGNORE INTO subscribers (email) VALUES (lower({email.to_sql_string}))") else + print error or else "?" + end + end + + # Remove `email` to the mailing list subscribers + fun unsubscribe(email: String) + do + assert execute("DELETE FROM subscribers WHERE email = lower({email.to_sql_string})") else + print error or else "?" + end + end +end + +redef class StatementRow + # Convert this BD row to a `Beer` + fun to_beer: Beer do return new Beer(self[0].to_s, self[1].to_s) +end diff --git a/contrib/benitlux/src/benitlux_model.nit b/contrib/benitlux/src/benitlux_model.nit new file mode 100644 index 0000000..57cb34e --- /dev/null +++ b/contrib/benitlux/src/benitlux_model.nit @@ -0,0 +1,93 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# Database and data model to be used by servers and clients +module benitlux_model + +import serialization + +# A beer, with a name and description +class Beer + auto_serializable + + init(name, desc: String) + do + self.name = name + self.desc = desc + end + + # Name of the beer + var name: String + + # Description on the Web site + var desc: String + + redef fun to_s do return "<{name}: {desc}>" +end + +# A collection of beer-related events +class BeerEvents + auto_serializable + + # New beers on the menu + var new_beers = new Array[Beer] + + # Beers that have left the menu + var gone_beers = new Array[Beer] + + # Beers that are on the menu today, and yesterday + var fix_beers = new Array[Beer] + + # Get a human pretty `Array[String]` version of `self` + fun to_email_content: Array[String] + do + var content = new Array[String] + + # New beers + var new_beers_name = new Array[String] + for beer in self.new_beers do + content.add "+ {beer.name}: {beer.desc}" + end + + # Gone beers + for beer in self.gone_beers do + content.add "- {beer.name}: {beer.desc}" + end + + # Fix beers + for beer in self.fix_beers do + content.add " {beer.name}: {beer.desc}" + end + + return content + end + + # Get a pretty and short version of `self` + fun to_email_title: String + do + var title = "Benelux Beer Menu" + + # New beers + var new_beers_name = new Array[String] + for beer in self.new_beers do new_beers_name.add beer.name + + if not new_beers_name.is_empty then + title += " (+ {new_beers_name.join(", ")})" + end + + return title + end +end diff --git a/contrib/benitlux/src/benitlux_view.nit b/contrib/benitlux/src/benitlux_view.nit new file mode 100644 index 0000000..6c5d53b --- /dev/null +++ b/contrib/benitlux/src/benitlux_view.nit @@ -0,0 +1,142 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# View logic of the Web interface Benitlux +module benitlux_view + +import benitlux_model +import template + +# Template for the whole Benitlux page +class BenitluxDocument + super Template + + # Page title + var page_title = "Benitlux Mailing List" is writable + + # Page header + fun header: Template do return new BenitluxHeader + + # Error or success message content, will be shown in a dismissable panel + var message_content: nullable String = null is writable + + # Error or success message level (success/danger/warning/info) + var message_level: nullable String = null is writable + + # Lines of the last email sent to subscribers + var sample_email_lines: nullable Array[String] = null is writable + + redef fun rendering + do + add """ + + + + + + + + """ + add page_title + add """ + + + """ + add header + add """ +
+ +
+
+

Service de diffusion des changements au menu de l'excellente + Brasserie Bénélux + sur la rue Sherbrooke. La liste est mise à jours tous les jours à 14h, + le courriel est envoyé au même moment.

+
+
+
+
@
+ +
+
+ + +
+
+
+ """ + + var message_level = message_level + var message_content = message_content + if message_level != null and message_content != null then + add """ + + """ + end + + var sample_email_lines = sample_email_lines + if sample_email_lines != null then + add """ +
+
Dernier courriel envoyé
+
    +
  • + {{{sample_email_lines.join("
  • ")}}} +
  • +
+
""" + end + + add """ +
+ +""" + end +end + +# Template for the header of Benitlux (right after the opening of ``) +class BenitluxHeader + super Template + + redef fun rendering + do + add """ +""" + end +end diff --git a/contrib/benitlux/src/benitlux_web.nit b/contrib/benitlux/src/benitlux_web.nit new file mode 100644 index 0000000..90e7d24 --- /dev/null +++ b/contrib/benitlux/src/benitlux_web.nit @@ -0,0 +1,37 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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 for Benitlux +module benitlux_web + +import benitlux_model +import benitlux_view +import benitlux_controller + +# Avoid executing when running tests +if "NIT_TESTING".environ == "true" then exit 0 + +var iface = "localhost:8080" + +var vh = new VirtualHost(iface) +vh.routes.add new Route("/rest/", new BenitluxRESTAction) +vh.routes.add new Route(null, new BenitluxSubscriptionAction) + +var factory = new HttpFactory.and_libevent +factory.config.virtual_hosts.add vh + +print "Launching server on http://{iface}/" +factory.run diff --git a/contrib/nitcc/src/grammar.nit b/contrib/nitcc/src/grammar.nit index 48c6195..fae7ed1 100644 --- a/contrib/nitcc/src/grammar.nit +++ b/contrib/nitcc/src/grammar.nit @@ -330,11 +330,11 @@ class Production # Is self transformed to a other production for the AST # FIXME: cleaup AST - var spe: nullable Production writable = null + var spe: nullable Production = null is writable # Is self contains only a single alternative (then no need for a abstract production class in the AST) # FIXME cleanup AST - var altone writable = false + var altone = false is writable # The cname of the class in the AST # FIXME: cleanup AST @@ -396,7 +396,7 @@ class Alternative var prod: Production # The name of the alternative - var name: String writable + var name: String is writable # The elements of the alternative var elems: Array[Element] @@ -424,13 +424,13 @@ class Alternative end # The code for the reduction - var codes: nullable Array[Code] writable = null + var codes: nullable Array[Code] = null is writable # Is the alternative transformed (ie not in the AST) - var trans writable = false + var trans = false is writable # Is the alternative unparsable? (ie not in the automaton) - var phony writable = false + var phony = false is writable # Imitialize codes with the elements fun make_codes diff --git a/contrib/online_ide/sources/nit/pnacl_nit.nit b/contrib/online_ide/sources/nit/pnacl_nit.nit index 07db5ed..f4a02f2 100644 --- a/contrib/online_ide/sources/nit/pnacl_nit.nit +++ b/contrib/online_ide/sources/nit/pnacl_nit.nit @@ -17,8 +17,8 @@ # A version of the naive Nit interpreter for PNaCl. module pnacl_nit -import naive_interpreter -import debugger +import interpreter::naive_interpreter +import interpreter::debugger import pnacl intrude import toolcontext intrude import modelbuilder diff --git a/contrib/pep8analysis/src/ast/pretty_instructions.nit b/contrib/pep8analysis/src/ast/pretty_instructions.nit index 488d382..98380f7 100644 --- a/contrib/pep8analysis/src/ast/pretty_instructions.nit +++ b/contrib/pep8analysis/src/ast/pretty_instructions.nit @@ -23,7 +23,7 @@ end class ASTPrinter super Visitor - var str writable = "" + var str = "" is writable init do end redef fun visit(n) do n.accept_ast_printer(self) diff --git a/contrib/pep8analysis/src/backbone.nit b/contrib/pep8analysis/src/backbone.nit index a7e1185..02042bd 100644 --- a/contrib/pep8analysis/src/backbone.nit +++ b/contrib/pep8analysis/src/backbone.nit @@ -73,7 +73,7 @@ class Warn redef fun prefix do return "Warning: " end -class Error +class P8Error super Note init (line: Location, msg: String) do super init range(from, to: Location, msg: String) do super diff --git a/contrib/pep8analysis/src/cfg/cfg_base.nit b/contrib/pep8analysis/src/cfg/cfg_base.nit index 5e8c856..c4de901 100644 --- a/contrib/pep8analysis/src/cfg/cfg_base.nit +++ b/contrib/pep8analysis/src/cfg/cfg_base.nit @@ -172,7 +172,7 @@ class CFG b.successors.add(db) db.predecessors.add(b) else - manager.notes.add(new Error(line.location, + manager.notes.add(new P8Error(line.location, "this instruction is not followed by valid code as it should (misplaced data or missing BR?)")) end end @@ -181,7 +181,7 @@ class CFG has_function_calls = true var next_addr = line.address+4 if not addr_to_blocks.has_key(next_addr) then - manager.notes.add(new Error(line.location, + manager.notes.add(new P8Error(line.location, "this CALL is not followed by valide code as it should")) else b.after_call = addr_to_blocks[next_addr] @@ -306,7 +306,7 @@ class CFG end end - var watchdog writable = 0 + var watchdog = 0 is writable fun link_ret_to_calls(b: BasicBlock, to_link_ori: List[BasicBlock], seq: List[BasicBlock], depth: Int): Bool do watchdog += 1 @@ -345,7 +345,7 @@ class CFG else if instr isa ARetInstruction then if to_link.is_empty then - manager.notes.add( new Error(instr.location,"no CALL can be linked to this RET") ) + manager.notes.add( new P8Error(instr.location,"no CALL can be linked to this RET") ) return false else var caller = to_link.pop diff --git a/contrib/pep8analysis/src/cfg/sanity.nit b/contrib/pep8analysis/src/cfg/sanity.nit index 842296f..2399bf4 100644 --- a/contrib/pep8analysis/src/cfg/sanity.nit +++ b/contrib/pep8analysis/src/cfg/sanity.nit @@ -64,13 +64,13 @@ redef class AnalysisManager if i == len-1 or line.address + line.size != lines[i+1].address then if error then if first == line then - manager.notes.add(new Error(first.location, msg)) + manager.notes.add(new P8Error(first.location, msg)) else - manager.notes.add(new Error.range(first.location, line.location, msg)) + manager.notes.add(new P8Error.range(first.location, line.location, msg)) end else if first == line then - manager.notes.add(new Error(first.location, msg)) + manager.notes.add(new P8Error(first.location, msg)) else manager.notes.add(new Warn.range(first.location, line.location, msg)) end diff --git a/contrib/pep8analysis/src/parser/lexer.nit b/contrib/pep8analysis/src/parser/lexer.nit index f1a3c44..f5b910a 100644 --- a/contrib/pep8analysis/src/parser/lexer.nit +++ b/contrib/pep8analysis/src/parser/lexer.nit @@ -6,14 +6,14 @@ intrude import parser_nodes private import tables redef class Token - var _text: nullable String + var text_cache: nullable String redef fun text do - var res = _text + var res = _text_cache if res != null then return res res = location.text - _text = res + _text_cache = res return res end @@ -245,7 +245,7 @@ redef class EOF init(loc: Location) do - _text = "" + _text_cache = "" _location = loc end end @@ -266,25 +266,25 @@ end class Lexer super TablesCapable # Last peeked token - var _token: nullable Token + var token: nullable Token # Lexer current state - var _state: Int = 0 + var state: Int = 0 # The source file - var _file: SourceFile + var file: SourceFile # Current character in the stream - var _stream_pos: Int = 0 + var stream_pos: Int = 0 # Current line number in the input stream - var _line: Int = 0 + var line: Int = 0 # Current column in the input stream - var _pos: Int = 0 + var pos: Int = 0 # Was the last character a cariage-return? - var _cr: Bool = false + var cr: Bool = false # Constante state values private fun state_initial: Int do return 0 end diff --git a/contrib/pep8analysis/src/parser/parser.nit b/contrib/pep8analysis/src/parser/parser.nit index 1c6b68f..4fd6be1 100644 --- a/contrib/pep8analysis/src/parser/parser.nit +++ b/contrib/pep8analysis/src/parser/parser.nit @@ -23,13 +23,13 @@ end class Parser super TablesCapable # Associated lexer - var _lexer: Lexer + var lexer: Lexer # Stack of pushed states and productions - var _stack: Array[State] + private var stack: Array[State] # Position in the stack - var _stack_pos: Int + private var stack_pos: Int # Create a new parser based on a given lexer init(lexer: Lexer) @@ -148,10 +148,10 @@ class Parser end end - var _reduce_table: Array[ReduceAction] + private var reduce_table: Array[ReduceAction] private fun build_reduce_table do - _reduce_table = new Array[ReduceAction].with_items( + reduce_table = new Array[ReduceAction].with_items( new ReduceAction0(0), new ReduceAction1(0), new ReduceAction2(0), @@ -194,11 +194,11 @@ end redef class Prod # Location on the first token after the start of a production # So outside the production for epilon production - var _first_location: nullable Location + var first_location: nullable Location # Location of the last token before the end of a production # So outside the production for epilon production - var _last_location: nullable Location + var last_location: nullable Location end # Find location of production nodes @@ -206,16 +206,16 @@ end private class ComputeProdLocationVisitor super Visitor # Currenlty visited productions that need a first token - var _need_first_prods: Array[Prod] = new Array[Prod] + var need_first_prods: Array[Prod] = new Array[Prod] # Already visited epsilon productions that waits something after them - var _need_after_epsilons: Array[Prod] = new Array[Prod] + var need_after_epsilons: Array[Prod] = new Array[Prod] # Already visited epsilon production that waits something before them - var _need_before_epsilons: Array[Prod] = new Array[Prod] + var need_before_epsilons: Array[Prod] = new Array[Prod] # Location of the last visited token in the current production - var _last_location: nullable Location = null + var last_location: nullable Location = null redef fun visit(n: nullable ANode) do @@ -305,7 +305,7 @@ private abstract class ReduceAction l1.append(l2) return l1 end - var _goto: Int + var goto: Int init(g: Int) do _goto = g end diff --git a/contrib/pep8analysis/src/parser/parser_nodes.nit b/contrib/pep8analysis/src/parser/parser_nodes.nit index e1fb641..97530a4 100644 --- a/contrib/pep8analysis/src/parser/parser_nodes.nit +++ b/contrib/pep8analysis/src/parser/parser_nodes.nit @@ -1,16 +1,14 @@ # Raw AST node hierarchy. # This file was generated by SableCC (http://www.sablecc.org/). -module parser_nodes is old_style_init +module parser_nodes import location # Root of the AST hierarchy abstract class ANode - var _location: nullable Location = null - # Location is set during AST building. Once built, location cannon be null # However, manual instanciated nodes may need mode care - fun location: Location do return _location.as(not null) + var location: Location is writable, noinit end # Ancestor of all tokens @@ -23,7 +21,6 @@ end # Ancestor of all productions abstract class Prod super ANode - fun location=(loc: Location) do _location = loc end class TEol super Token @@ -81,192 +78,130 @@ class TId end class EOF super Token - #private init noinit do end end class AError super EOF - #private init noinit do end end class ALine super Prod - var _n_label_decl: nullable ALabelDecl = null - fun n_label_decl: nullable ALabelDecl do return _n_label_decl - var _n_comment: nullable TComment = null - fun n_comment: nullable TComment do return _n_comment + var n_label_decl: nullable ALabelDecl = null + var n_comment: nullable TComment = null end class AInstruction super Prod - var _n_id: TId - fun n_id: TId do return _n_id - fun n_id=(n_id: TId) do _n_id = n_id - init do end + var n_id: TId is noinit end class AOperand super Prod - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_value: AValue is noinit end class AValue super Prod end class ADirective super Prod end class AListing super Prod - var _n_lines: List[ALine] = new List[ALine] - fun n_lines: List[ALine] do return _n_lines - var _n_label_decl: nullable ALabelDecl = null - fun n_label_decl: nullable ALabelDecl do return _n_label_decl - var _n_end_block: TEndBlock - fun n_end_block: TEndBlock do return _n_end_block - init do end + var n_lines: List[ALine] = new List[ALine] + var n_label_decl: nullable ALabelDecl = null + var n_end_block: TEndBlock is noinit end class AEmptyLine super ALine - var _n_eol: TEol - fun n_eol: TEol do return _n_eol - init do end + var n_eol: TEol is noinit end abstract class ANonEmptyLine super ALine end class AInstructionLine super ANonEmptyLine - var _n_instruction: AInstruction - fun n_instruction: AInstruction do return _n_instruction - var _n_eol: TEol - fun n_eol: TEol do return _n_eol - init do end + var n_instruction: AInstruction is noinit + var n_eol: TEol is noinit end class ADirectiveLine super ANonEmptyLine - var _n_directive: ADirective - fun n_directive: ADirective do return _n_directive - var _n_eol: TEol - fun n_eol: TEol do return _n_eol - init do end + var n_directive: ADirective is noinit + var n_eol: TEol is noinit end class ALabelDecl super Prod - var _n_id: TId - fun n_id: TId do return _n_id - var _n_colon: TColon - fun n_colon: TColon do return _n_colon - init do end + var n_id: TId is noinit + var n_colon: TColon is noinit end class AUnaryInstruction super AInstruction end class ABinaryInstruction super AInstruction - var _n_operand: AOperand - fun n_operand: AOperand do return _n_operand - init do end + var n_operand: AOperand is noinit end class AImmediateOperand super AOperand end class AAnyOperand super AOperand - var _n_comma: TComma - fun n_comma: TComma do return _n_comma - var _n_id: TId - fun n_id: TId do return _n_id - init do end + var n_comma: TComma is noinit + var n_id: TId is noinit end class ALabelValue super AValue - var _n_id: TId - fun n_id: TId do return _n_id - init do end + var n_id: TId is noinit end class ANumberValue super AValue - var _n_number: TNumber - fun n_number: TNumber do return _n_number - init do end + var n_number: TNumber is noinit end class ACharValue super AValue - var _n_char: TChar - fun n_char: TChar do return _n_char - init do end + var n_char: TChar is noinit end class AStringValue super AValue - var _n_string: TString - fun n_string: TString do return _n_string - init do end + var n_string: TString is noinit end class AHexValue super AValue - var _n_hex: THex - fun n_hex: THex do return _n_hex - init do end + var n_hex: THex is noinit end class AByteDirective super ADirective - var _n_tk_byte: TTkByte - fun n_tk_byte: TTkByte do return _n_tk_byte - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_tk_byte: TTkByte is noinit + var n_value: AValue is noinit end class AWordDirective super ADirective - var _n_tk_word: TTkWord - fun n_tk_word: TTkWord do return _n_tk_word - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_tk_word: TTkWord is noinit + var n_value: AValue is noinit end class ABlockDirective super ADirective - var _n_tk_block: TTkBlock - fun n_tk_block: TTkBlock do return _n_tk_block - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_tk_block: TTkBlock is noinit + var n_value: AValue is noinit end class AAsciiDirective super ADirective - var _n_tk_ascii: TTkAscii - fun n_tk_ascii: TTkAscii do return _n_tk_ascii - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_tk_ascii: TTkAscii is noinit + var n_value: AValue is noinit end class AAddrssDirective super ADirective - var _n_tk_addrss: TTkAddrss - fun n_tk_addrss: TTkAddrss do return _n_tk_addrss - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_tk_addrss: TTkAddrss is noinit + var n_value: AValue is noinit end class AEquateDirective super ADirective - var _n_tk_equate: TTkEquate - fun n_tk_equate: TTkEquate do return _n_tk_equate - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_tk_equate: TTkEquate is noinit + var n_value: AValue is noinit end class ABurnDirective super ADirective - var _n_tk_burn: TTkBurn - fun n_tk_burn: TTkBurn do return _n_tk_burn - var _n_value: AValue - fun n_value: AValue do return _n_value - init do end + var n_tk_burn: TTkBurn is noinit + var n_value: AValue is noinit end class Start super Prod - var _n_base: nullable AListing - fun n_base: nullable AListing do return _n_base - var _n_eof: EOF - fun n_eof: EOF do return _n_eof + var n_base: nullable AListing + var n_eof: EOF is noinit init(n_base: nullable AListing, n_eof: EOF) do super diff --git a/contrib/pep8analysis/src/parser/parser_prod.nit b/contrib/pep8analysis/src/parser/parser_prod.nit index 8f71631..f7fb2f7 100644 --- a/contrib/pep8analysis/src/parser/parser_prod.nit +++ b/contrib/pep8analysis/src/parser/parser_prod.nit @@ -8,9 +8,7 @@ private import tables redef class ANode # Parent of the node in the AST - var _parent: nullable ANode - fun parent: nullable ANode do return _parent - fun parent=(p: nullable ANode) do _parent = p + var parent: nullable ANode is writable # Remove a child from the AST fun remove_child(child: ANode) diff --git a/contrib/pep8analysis/src/parser/xss/parser.xss b/contrib/pep8analysis/src/parser/xss/parser.xss index 7e24374..435ced7 100644 --- a/contrib/pep8analysis/src/parser/xss/parser.xss +++ b/contrib/pep8analysis/src/parser/xss/parser.xss @@ -20,10 +20,10 @@ $ template make_parser() # State of the parser automata as stored in the parser stack. private class State # The internal state number - readable writable var _state: Int + readable var _state: Int is writable # The node stored with the state in the stack - readable writable var _nodes: nullable Object + readable var _nodes: nullable Object is writable init(state: Int, nodes: nullable Object) do diff --git a/contrib/pep8analysis/src/pep8analysis_web.nit b/contrib/pep8analysis/src/pep8analysis_web.nit index 6167d47..30c0849 100644 --- a/contrib/pep8analysis/src/pep8analysis_web.nit +++ b/contrib/pep8analysis/src/pep8analysis_web.nit @@ -31,6 +31,7 @@ import ast import model import cfg import flow_analysis +intrude import standard::stream in "C++" `{ #include diff --git a/contrib/pep8analysis/www/index.html b/contrib/pep8analysis/www/index.html index 3a259df..085a301 100644 --- a/contrib/pep8analysis/www/index.html +++ b/contrib/pep8analysis/www/index.html @@ -127,16 +127,19 @@ - Pep/8 Analysis + Xymus.net diff --git a/examples/leapfrog/leapfrog.nit b/examples/leapfrog/leapfrog.nit index ac8cff0..6aa6f48 100644 --- a/examples/leapfrog/leapfrog.nit +++ b/examples/leapfrog/leapfrog.nit @@ -34,7 +34,7 @@ class Animal # The value indicate the number of step that remain to be stunt # # If a animal is stunned, it cannot move horizontally - var stunt_ttl: Int writable = 0 + var stunt_ttl: Int = 0 is writable # Common update for animal # handle stunt and edge collision diff --git a/examples/mnit_dino/src/game_logic.nit b/examples/mnit_dino/src/game_logic.nit index b148778..1df0119 100644 --- a/examples/mnit_dino/src/game_logic.nit +++ b/examples/mnit_dino/src/game_logic.nit @@ -182,7 +182,7 @@ end class MovingEntity super Entity - var going_to : nullable GamePos writable = null + var going_to : nullable GamePos = null is writable fun speed : Int is abstract diff --git a/examples/nitcorn/Makefile b/examples/nitcorn/Makefile index 96a6ee3..1c55458 100644 --- a/examples/nitcorn/Makefile +++ b/examples/nitcorn/Makefile @@ -1,3 +1,7 @@ all: mkdir -p bin/ ../../bin/nitg --dir bin src/nitcorn_hello_world.nit src/file_server_on_port_80.nit + +xymus.net: + mkdir -p bin/ + ../../bin/nitg --dir bin/ -I ../../contrib/tnitter/src/ -I ../../contrib/benitlux/src/ src/xymus_net.nit diff --git a/examples/nitcorn/src/xymus_net.nit b/examples/nitcorn/src/xymus_net.nit new file mode 100644 index 0000000..9c4b53c --- /dev/null +++ b/examples/nitcorn/src/xymus_net.nit @@ -0,0 +1,150 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# Configuration of the Web server of xymus.net +module xymus_net + +import nitcorn +import privileges + +# Use actions defined by contribs +import tnitter +import benitlux_controller + +# Header for the whole site +class MasterHeader + super Template + + # Current page id, is used to set the `active` class on the header tab + var page: nullable String + + # Make room for a login or logout placeholder (used by tnitter) + var login_placeholder: Bool + + redef fun rendering + do + var actives = new HashMap[String, String] + var page = page + if page != null then actives[page] = " class=\"active\"" + + add """ +""" + end + + redef fun to_s do return write_to_string +end + +redef class Tnitter + redef var header: String = (new MasterHeader("tnitter", true)).to_s +end + +redef class BenitluxDocument + redef var header = new MasterHeader("benitlux", false) +end + +redef class ErrorTemplate + redef var header = new MasterHeader(null, false) +end + +# Avoid executing when running tests +if "NIT_TESTING".environ == "true" then exit 0 + +# Setup server interfaces +# +# Change these to use this config on your own server! +var default_vh = new VirtualHost("xymus.net:80") +var vps_vh = new VirtualHost("vps.xymus.net:80") +var tnitter_vh = new VirtualHost("tnitter.xymus.net:80") +var pep8_vh = new VirtualHost("pep8.xymus.net:80") +var benitlux_vh = new VirtualHost("benitlux.xymus.net:80") + +var factory = new HttpFactory.and_libevent +factory.config.virtual_hosts.add default_vh +factory.config.virtual_hosts.add vps_vh +factory.config.virtual_hosts.add tnitter_vh +factory.config.virtual_hosts.add pep8_vh +factory.config.virtual_hosts.add benitlux_vh + +# Ports are open, drop to a low-privileged user if we are root +var user_group = new UserGroup("nitcorn", "nitcorn") +if sys.uid == 0 then user_group.drop_privileges + +# Tnitter is available at `tnitter.xymus.net` and `xymus.net/tnitter/` +var tnitter = new Tnitter +default_vh.routes.add new Route("/tnitter/", tnitter) +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/` +# +# TODO Implement pep8analysis server-side with a nitcorn action +pep8_vh.routes.add new Route(null, new FileServer("/var/www/pep8/")) + +# Benitlux is available at `benitlux.xymus.net` and `xymus.net/benitlux/` +# +# It has 2 actions/Web interfaces. The Web user UI to subscribe and unsubscribe +# to the mailing list. And the RESTful interface used by the Android app. +var benitlux_sub = new BenitluxSubscriptionAction +var benitlux_rest = new BenitluxRESTAction +default_vh.routes.add new Route("/benitlux/rest/", benitlux_rest) +default_vh.routes.add new Route("/benitlux/", benitlux_sub) +benitlux_vh.routes.add new Route("/rest/", benitlux_rest) +benitlux_vh.routes.add new Route(null, benitlux_sub) + +# 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/") +file_server_ens.header = (new MasterHeader("ens", false)) +default_vh.routes.add new Route("/ens/", file_server_ens) + +# Default file server is used for the main page at `xymus.net` and it is +# the default action for any path not caught by other actions. +var file_server = new FileServer("/var/www/") +file_server.header = (new MasterHeader(null, false)) +default_vh.routes.add new Route(null, file_server) +vps_vh.routes.add new Route(null, file_server) + +# Launch server main loop +factory.run diff --git a/examples/shoot/Makefile b/examples/shoot/Makefile index ebba097..5e564d7 100644 --- a/examples/shoot/Makefile +++ b/examples/shoot/Makefile @@ -12,5 +12,9 @@ null: mkdir -p bin ../../bin/nitc -o bin/shoot_null src/shoot_null.nit +images: + mkdir -p assets/images + ../mnit_dino/tools/svg-to-pngs art/ships.svg assets/ + clean: rm -rf bin diff --git a/examples/shoot/art/bio.png b/examples/shoot/art/bio.png new file mode 100644 index 0000000..db53c55 Binary files /dev/null and b/examples/shoot/art/bio.png differ diff --git a/examples/shoot/art/blue.png b/examples/shoot/art/blue.png new file mode 100644 index 0000000..703d02f Binary files /dev/null and b/examples/shoot/art/blue.png differ diff --git a/examples/shoot/art/fighter.png b/examples/shoot/art/fighter.png new file mode 100755 index 0000000..f22213b Binary files /dev/null and b/examples/shoot/art/fighter.png differ diff --git a/examples/shoot/art/ships.svg b/examples/shoot/art/ships.svg new file mode 100644 index 0000000..6d90fee --- /dev/null +++ b/examples/shoot/art/ships.svg @@ -0,0 +1,41377 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/shoot/assets/boss.png b/examples/shoot/assets/boss.png index c74ea75..49cf0c2 100644 Binary files a/examples/shoot/assets/boss.png and b/examples/shoot/assets/boss.png differ diff --git a/examples/shoot/assets/boss_left.png b/examples/shoot/assets/boss_left.png index f318078..c9de48a 100644 Binary files a/examples/shoot/assets/boss_left.png and b/examples/shoot/assets/boss_left.png differ diff --git a/examples/shoot/assets/boss_right.png b/examples/shoot/assets/boss_right.png index 08e4406..7901bdf 100644 Binary files a/examples/shoot/assets/boss_right.png and b/examples/shoot/assets/boss_right.png differ diff --git a/examples/shoot/assets/enemy0.png b/examples/shoot/assets/enemy0.png index b94152f..088d683 100644 Binary files a/examples/shoot/assets/enemy0.png and b/examples/shoot/assets/enemy0.png differ diff --git a/examples/shoot/assets/enemy1.png b/examples/shoot/assets/enemy1.png index f01616c..c250e0b 100644 Binary files a/examples/shoot/assets/enemy1.png and b/examples/shoot/assets/enemy1.png differ diff --git a/examples/shoot/assets/enemy2.png b/examples/shoot/assets/enemy2.png index a59c906..28e1ab1 100644 Binary files a/examples/shoot/assets/enemy2.png and b/examples/shoot/assets/enemy2.png differ diff --git a/examples/shoot/assets/enemy3.png b/examples/shoot/assets/enemy3.png index 894057e..da510e4 100644 Binary files a/examples/shoot/assets/enemy3.png and b/examples/shoot/assets/enemy3.png differ diff --git a/examples/shoot/assets/enemy4.png b/examples/shoot/assets/enemy4.png index b033122..c4c2ac3 100644 Binary files a/examples/shoot/assets/enemy4.png and b/examples/shoot/assets/enemy4.png differ diff --git a/examples/shoot/assets/enemy4_turret.png b/examples/shoot/assets/enemy4_turret.png index 5a9ec70..1afd8a4 100644 Binary files a/examples/shoot/assets/enemy4_turret.png and b/examples/shoot/assets/enemy4_turret.png differ diff --git a/examples/shoot/assets/enemy_kamikaze.png b/examples/shoot/assets/enemy_kamikaze.png index 7b00e6d..d2f64e3 100644 Binary files a/examples/shoot/assets/enemy_kamikaze.png and b/examples/shoot/assets/enemy_kamikaze.png differ diff --git a/examples/shoot/assets/enemy_missile.png b/examples/shoot/assets/enemy_missile.png index c73706c..8a167bc 100644 Binary files a/examples/shoot/assets/enemy_missile.png and b/examples/shoot/assets/enemy_missile.png differ diff --git a/examples/shoot/assets/enemy_shoot.png b/examples/shoot/assets/enemy_shoot.png index 5c89168..6282d8e 100644 Binary files a/examples/shoot/assets/enemy_shoot.png and b/examples/shoot/assets/enemy_shoot.png differ diff --git a/examples/shoot/assets/player.png b/examples/shoot/assets/player.png index 2de11bf..d522fcc 100644 Binary files a/examples/shoot/assets/player.png and b/examples/shoot/assets/player.png differ diff --git a/examples/shoot/assets/player_missile.png b/examples/shoot/assets/player_missile.png index ca6bedb..0ea26ca 100644 Binary files a/examples/shoot/assets/player_missile.png and b/examples/shoot/assets/player_missile.png differ diff --git a/examples/shoot/assets/player_shoot.png b/examples/shoot/assets/player_shoot.png old mode 100644 new mode 100755 index 68e7fe6..db5d808 Binary files a/examples/shoot/assets/player_shoot.png and b/examples/shoot/assets/player_shoot.png differ diff --git a/examples/shoot/src/shoot_logic.nit b/examples/shoot/src/shoot_logic.nit index 3443a47..838c78b 100644 --- a/examples/shoot/src/shoot_logic.nit +++ b/examples/shoot/src/shoot_logic.nit @@ -36,17 +36,17 @@ class Player end # Current forture of the player - var money: Int writable = 0 + var money: Int = 0 is writable # Number of basic bullets fired together - var nbshoots: Int writable = 1 + var nbshoots: Int = 1 is writable # Time bebore the player shoot again a basic bullet (cooldown) # Shoot if 0 var shoot_ttl = 0 # Number of missiles - var nbmissiles: Int writable = 0 + var nbmissiles: Int = 0 is writable # Time bebore the player shoot again a missile (cooldown) # Shoot if 0 @@ -152,7 +152,7 @@ class GoingTarget super Hitable # true in on move, false if player is at rest - var active writable = false + var active = false is writable init do self.width = 500 @@ -319,12 +319,24 @@ class Enemy0 super Enemy redef fun loot do return 3 + + redef init(scene) + do + self.width = 3600 + self.height = 3600 + end end -# Simple shooter of paris of basic bullets +# Simple shooter of pairs of basic bullets class Enemy1 super Enemy + redef init(scene) + do + self.width = 4400 + self.height = 4400 + end + redef fun shoot do # Next shoot @@ -348,6 +360,12 @@ end class Enemy2 super Enemy + redef init(scene) + do + self.width = 6000 + self.height = 6000 + end + redef fun shoot do # Next shoot @@ -366,10 +384,16 @@ class Enemy2 redef fun loot do return 10 end -# Enem that shoot rings of basic bullets +# Enemy that shoot rings of basic bullets class Enemy3 super Enemy + redef init(scene) + do + self.width = 5800 + self.height = 5800 + end + redef fun shoot do # Next shoot @@ -395,6 +419,12 @@ class Enemy4 # The angle of the turret var angle: Float = 0.0 + redef init(scene) + do + self.width = 4200 + self.height = 4200 + end + redef fun update do super @@ -437,6 +467,12 @@ end class EnemyKamikaze super Enemy + redef init(scene) + do + self.width = 3200 + self.height = 3200 + end + redef fun update do super @@ -465,12 +501,12 @@ class Boss init(scene) do super - self.width = 128 * 100 - self.height = 100 * 100 + self.width = 140 * 100 + self.height = 96 * 100 self.x = scene.width / 2 self.y = -100 * 100 - self.left_part = new BossPart(self, -48*100) - self.right_part = new BossPart(self, 48*100) + self.left_part = new BossPart(self, -66*100) + self.right_part = new BossPart(self, 66*100) end var flick_ttl: Int = 0 @@ -560,8 +596,8 @@ class BossPart self.boss = boss self.relx = relx super(boss.scene) - self.width = 32 * 100 - self.height = 60 * 100 + self.width = 38 * 100 + self.height = 48 * 100 # Alternate the shoots of the arms if relx > 0 then @@ -798,13 +834,13 @@ class ShotScene super Scene # When a scene need to be replaced, just assign the next_scene to a non null value - var next_scene: nullable ShotScene writable = null + var next_scene: nullable ShotScene = null is writable # The width of the whole scene - var width: Int writable + var width: Int is writable # The height of the whole scene - var height: Int writable + var height: Int is writable init(w,h: Int) do @@ -998,7 +1034,7 @@ class MenuScene end end - var play: Bool writable = false + var play: Bool = false is writable var ttl: Int = 50 redef fun update diff --git a/lib/android/assets_and_resources.nit b/lib/android/assets_and_resources.nit index d654641..ba304bd 100644 --- a/lib/android/assets_and_resources.nit +++ b/lib/android/assets_and_resources.nit @@ -25,8 +25,8 @@ module assets_and_resources import native_app_glue -import java_io import java +import java::io in "Java" `{ import android.content.res.AssetManager; diff --git a/lib/android/audio.nit b/lib/android/audio.nit index 480238f..8d3264f 100644 --- a/lib/android/audio.nit +++ b/lib/android/audio.nit @@ -22,7 +22,7 @@ module audio import java -import java_io +import java::io import assets_and_resources import app @@ -137,25 +137,25 @@ end class SoundPool private var nsoundpool: NativeSoundPool is noinit # The maximum number of simultaneous streams for this SoundPool - var max_streams writable = 10 + var max_streams = 10 is writable # The audio stream type, 3 is STREAM_MUSIC, default for game application - var stream_type writable = 3 + var stream_type = 3 is writable # The sample-rate converter quality, currently has no effect - var src_quality writable = 0 + var src_quality = 0 is writable # Left volume value, range 0.0 to 1.0 - var left_volume writable = 1.0 + var left_volume = 1.0 is writable # Right volume value, range 0.0 to 1.0 - var right_volume writable = 1.0 + var right_volume = 1.0 is writable # Playback rate, 1.0 = normal playback, range 0.5 to 2.0 - var rate writable = 1.0 + var rate = 1.0 is writable # Loop mode, 0 = no loop, -1 = loop forever - var looping writable = 0 + var looping = 0 is writable # Stream priority private var priority = 1 diff --git a/lib/android/sensors.nit b/lib/android/sensors.nit index 2246298..4c28466 100644 --- a/lib/android/sensors.nit +++ b/lib/android/sensors.nit @@ -128,9 +128,9 @@ end # NIT representation of an Android Sensor used in android_app to initialize sensors class AndroidSensor - var asensor writable = new ASensor - var enabled writable = false - var event_rate writable = 100000 + var asensor = new ASensor is writable + var enabled = false is writable + var event_rate = 100000 is writable fun name: String do return asensor.name.to_s fun vendor: String do return asensor.vendor.to_s @@ -234,7 +234,7 @@ redef class App var proximity = new AndroidSensor var sensormanager: ASensorManager var eventqueue: ASensorEventQueue - var sensors_support_enabled writable = false + var sensors_support_enabled = false is writable private fun extern_input_sensor_accelerometer(event: ASensorAccelerometer) do input(event) private fun extern_input_sensor_magnetic_field(event: ASensorMagneticField) do input(event) diff --git a/lib/bucketed_game.nit b/lib/bucketed_game.nit index cdedc32..b680079 100644 --- a/lib/bucketed_game.nit +++ b/lib/bucketed_game.nit @@ -104,14 +104,14 @@ end # Game logic on the client class ThinGame - var tick: Int protected writable = 0 + var tick: Int = 0 is protected writable end # Game turn on the client class ThinGameTurn[G: ThinGame] - var tick: Int protected writable = 0 + var tick: Int = 0 is protected writable - var events: List[GameEvent] protected writable = new List[GameEvent] + var events: List[GameEvent] = new List[GameEvent] is protected writable init (t: Int) do tick = t end diff --git a/lib/c.nit b/lib/c.nit index c0bd650..fdc7e99 100644 --- a/lib/c.nit +++ b/lib/c.nit @@ -16,6 +16,8 @@ # Utilities and performant structure for the FFI with C module c +import standard +intrude import standard::collection::array # A thin wrapper around a `NativeCArray` adding length information abstract class CArray[E] diff --git a/lib/csv.nit b/lib/csv.nit index 9b4da2f..82fbb05 100644 --- a/lib/csv.nit +++ b/lib/csv.nit @@ -19,7 +19,7 @@ module csv class CSVDocument super Streamable - var header: Array[String] writable = new Array[String] + var header: Array[String] = new Array[String] is writable var lines: Array[Array[String]] = new Array[Array[String]] fun set_header(values: Object...) do diff --git a/lib/curl/curl.nit b/lib/curl/curl.nit index d0c445d..bca105c 100644 --- a/lib/curl/curl.nit +++ b/lib/curl/curl.nit @@ -40,7 +40,7 @@ end # CURL Request class CurlRequest - var verbose: Bool writable = false + var verbose: Bool = false is writable private var curl: nullable Curl = null # Launch request method @@ -76,8 +76,8 @@ class CurlHTTPRequest super CurlCallbacksRegisterIntern var url: String - var datas: nullable HeaderMap writable = null - var headers: nullable HeaderMap writable = null + var datas: nullable HeaderMap = null is writable + var headers: nullable HeaderMap = null is writable # Set the user agent for all following HTTP requests fun user_agent=(name: String) @@ -201,14 +201,14 @@ class CurlMailRequest super CurlRequest super CCurlCallbacks - var headers: nullable HeaderMap writable = null - var headers_body: nullable HeaderMap writable = null - var from: nullable String writable = null - var to: nullable Array[String] writable = null - var cc: nullable Array[String] writable = null - var bcc: nullable Array[String] writable = null - var subject: nullable String writable = "" - var body: nullable String writable = "" + var headers: nullable HeaderMap = null is writable + var headers_body: nullable HeaderMap = null is writable + var from: nullable String = null is writable + var to: nullable Array[String] = null is writable + var cc: nullable Array[String] = null is writable + var bcc: nullable Array[String] = null is writable + var subject: nullable String = "" is writable + var body: nullable String = "" is writable private var supported_outgoing_protocol: Array[String] = ["smtp", "smtps"] init (curl: nullable Curl) @@ -338,7 +338,7 @@ end # Callbacks attributes abstract class CurlCallbacksRegisterIntern - var delegate: nullable CurlCallbacks writable = null + var delegate: nullable CurlCallbacks = null is writable end # Abstract Curl request response diff --git a/lib/dummy_array.nit b/lib/dummy_array.nit index f939e49..21da4bd 100644 --- a/lib/dummy_array.nit +++ b/lib/dummy_array.nit @@ -13,11 +13,10 @@ class DummyArray super Set[Int] super ArrayCapable[Int] - var _capacity: Int - var _length: Int - redef fun length do return _length - var _keys: NativeArray[Int] - var _values: NativeArray[Int] + private var capacity: Int + redef var length: Int + private var keys: NativeArray[Int] + private var values: NativeArray[Int] redef fun add(value: Int) do @@ -87,8 +86,8 @@ end class DummyIterator super Iterator[Int] - var _array: DummyArray - var _pos: Int + private var array: DummyArray + private var pos: Int redef fun item: Int do diff --git a/lib/egl.nit b/lib/egl.nit index d49ee03..e349a0d 100644 --- a/lib/egl.nit +++ b/lib/egl.nit @@ -21,7 +21,7 @@ # C library. If a method or class is not documented in Nit, refer to # the official documentation by the Khronos Group at: # http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/ -module egl is pkgconfig("egl") +module egl is pkgconfig in "C Header" `{ #include diff --git a/lib/filter_stream.nit b/lib/filter_stream.nit index 32e5728..2797b67 100644 --- a/lib/filter_stream.nit +++ b/lib/filter_stream.nit @@ -38,7 +38,7 @@ end class StreamCat super FilterIStream - var _streams: Iterator[IStream] + private var streams: Iterator[IStream] redef fun eof: Bool do @@ -91,7 +91,7 @@ end class StreamDemux super FilterOStream - var _streams: Array[OStream] + private var streams: Array[OStream] redef fun is_writable: Bool do diff --git a/lib/for_abuse.nit b/lib/for_abuse.nit index 40cb1bf..2ee4d28 100644 --- a/lib/for_abuse.nit +++ b/lib/for_abuse.nit @@ -79,7 +79,7 @@ class CompareQuery[E] # The second element to compare var b: E # The result of the comparison (according to the user) - var res writable = 0 + var res = 0 is writable end # Abuser for sorting array, see `sort_fa` diff --git a/lib/glesv2.nit b/lib/glesv2.nit index fd33d63..a039717 100644 --- a/lib/glesv2.nit +++ b/lib/glesv2.nit @@ -20,7 +20,7 @@ # C library. If a method or class is not documented in Nit, refer to # the official documentation by the Khronos Group at: # http://www.khronos.org/opengles/sdk/docs/man/ -module glesv2 is pkgconfig("glesv2") +module glesv2 is pkgconfig in "C Header" `{ #include diff --git a/lib/hash_debug.nit b/lib/hash_debug.nit index d25c671..b618729 100644 --- a/lib/hash_debug.nit +++ b/lib/hash_debug.nit @@ -125,7 +125,7 @@ redef class HashCollection[K,N] redef fun node_at_idx(i,k) do sys.gt_count += 1 - sys.gt_tot_length += _length + sys.gt_tot_length += _the_length sys.gt_tot_cap += _capacity var c = _array[i] if c != null and c._next_in_bucklet != null then gt_collide(i,k) @@ -150,7 +150,7 @@ redef class HashCollection[K,N] do sys.st_count += 1 if _array[i] != null then st_collide(i,n) - sys.st_tot_length += _length + sys.st_tot_length += _the_length sys.st_tot_cap += _capacity super diff --git a/lib/ini.nit b/lib/ini.nit index 232e7c5..a6de8f1 100644 --- a/lib/ini.nit +++ b/lib/ini.nit @@ -284,7 +284,7 @@ end private class ConfigNode var parent: nullable ConfigNode var children = new HashMap[String, ConfigNode] - var name: String writable + var name: String is writable var value: nullable String init(name: String) do diff --git a/lib/android/java_io.nit b/lib/java/io.nit similarity index 94% rename from lib/android/java_io.nit rename to lib/java/io.nit index bcd70dc..04245a5 100644 --- a/lib/android/java_io.nit +++ b/lib/java/io.nit @@ -1,4 +1,4 @@ -# this file is part of NIT ( http://www.nitlanguage.org ). +# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2014 Romain Chanoir # @@ -14,10 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -# java io services +# Services from the `java.io` package # -# this module is used by `assets_and_resources` and `audio` for advanced purposes -module java_io +# This module is used by `android::assets_and_resources` and `android::audio`. +module io import java @@ -28,7 +28,6 @@ in "Java" `{ import java.io.FileDescriptor; import java.io.IOException; import java.io.SyncFailedException; - import android.util.Log; `} extern class NativeFile in "Java" `{ java.io.File `} @@ -42,7 +41,6 @@ extern class NativeFile in "Java" `{ java.io.File `} try { return recv.createNewFile(); }catch(IOException e){ - Log.e("Can't create file", e.getMessage()); e.printStackTrace(); return false; } @@ -56,7 +54,6 @@ extern class NativeFile in "Java" `{ java.io.File `} try { return recv.getCanonicalFile(); }catch(IOException e){ - Log.e("Can't create file", e.getMessage()); e.printStackTrace(); return null; } diff --git a/lib/java.nit b/lib/java/java.nit similarity index 99% rename from lib/java.nit rename to lib/java/java.nit index a3da38d..d9d1f27 100644 --- a/lib/java.nit +++ b/lib/java/java.nit @@ -24,7 +24,7 @@ # of `Sys::jni_env`, and multiple JVM using `Sys::jvm`. # # The module `jvm` gives more control over the JVM instances and wraps -# most of JNI functions. You can use it to further customize the behavior +# most of JNI functions. You can use it to further customize the behavior # of your code. module java is c_compiler_option("-I $(JAVA_HOME)/include/") @@ -173,7 +173,7 @@ redef extern class JavaObject var jni_env = sys.jni_env return pop_from_local_frame_with_env(jni_env) end - + private fun pop_from_local_frame_with_env(jni_env: JniEnv): SELF `{ return (*jni_env)->PopLocalFrame(jni_env, recv); `} diff --git a/lib/markdown/README b/lib/markdown/README new file mode 100644 index 0000000..a5c6052 --- /dev/null +++ b/lib/markdown/README @@ -0,0 +1,29 @@ +A markdown parser for Nit. + +Markdown documentation can be found in http://daringfireball.net/projects/markdown/. +This parser is inspired by the famous TxtMark for Java (https://github.com/rjeschke/txtmark). + +## Usage + +`nitmd` can be used as a standalone tool: + + $ nitmd file.md + +Or you can use it programmatically by importing the `markdown` module. + +## Differences with Markdown specification + +This parser passes all tests inside http://daringfireball.net/projects/downloads/MarkdownTest_1.0_2007-05-09.tgz execpt of two: + +1. Images.text: fails because this parser doesn't produce empty 'title' image attributes. +2. Literal quotes in titles.text: because markdown accepts unescaped quotes in titles and this is wrong. + +## Testing + +The NitUnit test suite can be found in `test_markdown.nit`. + +Minimalists tests are prefixed with `process_*`. All tests from daringfireball are prefixed with `process_daring*`. + +Run the test suite: + + $ nitunit lib/markdown/markdown.nit -t lib/markdown/test_markdown.nit diff --git a/lib/markdown/markdown.nit b/lib/markdown/markdown.nit new file mode 100644 index 0000000..336fc3d --- /dev/null +++ b/lib/markdown/markdown.nit @@ -0,0 +1,2264 @@ +# 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. + +# Markdown parsing. +module markdown + +import template + +# Parse a markdown string and split it in blocks. +# +# Blocks are then outputed by an `MarkdownEmitter`. +# +# Usage: +# +# var proc = new MarkdownProcessor +# var html = proc.process("**Hello World!**") +# assert html == "

Hello World!

\n" +# +# SEE: `String::md_to_html` for a shortcut. +class MarkdownProcessor + + var emitter: MarkdownEmitter is noinit + + init do self.emitter = new MarkdownEmitter(self) + + # Process the mardown `input` string and return the processed output. + fun process(input: String): Streamable do + # init processor + link_refs.clear + last_link_ref = null + current_line = null + current_block = null + # parse markdown + var parent = read_lines(input) + parent.remove_surrounding_empty_lines + recurse(parent, false) + # output processed text + return emitter.emit(parent.kind) + end + + # Split `input` string into `MDLines` and create a parent `MDBlock` with it. + private fun read_lines(input: String): MDBlock do + var block = new MDBlock + var value = new FlatBuffer + var i = 0 + while i < input.length do + value.clear + var pos = 0 + var eol = false + while not eol and i < input.length do + var c = input[i] + if c == '\n' then + i += 1 + eol = true + else if c == '\t' then + var np = pos + (4 - (pos.bin_and(3))) + while pos < np do + value.add ' ' + pos += 1 + end + i += 1 + else + pos += 1 + value.add c + i += 1 + end + end + + var line = new MDLine(value.write_to_string) + var is_link_ref = check_link_ref(line) + # Skip link refs + if not is_link_ref then block.add_line line + end + return block + end + + # Check if line is a block link definition. + # Return `true` if line contains a valid link ref and save it into `link_refs`. + private fun check_link_ref(line: MDLine): Bool do + var md = line.value + var is_link_ref = false + var id = new FlatBuffer + var link = new FlatBuffer + var comment = new FlatBuffer + var pos = -1 + if not line.is_empty and line.leading < 4 and line.value[line.leading] == '[' then + pos = line.leading + 1 + pos = md.read_until(id, pos, ']') + if not id.is_empty and pos + 2 < line.value.length then + if line.value[pos + 1] == ':' then + pos += 2 + pos = md.skip_spaces(pos) + if line.value[pos] == '<' then + pos += 1 + pos = md.read_until(link, pos, '>') + pos += 1 + else + pos = md.read_until(link, pos, ' ', '\n') + end + if not link.is_empty then + pos = md.skip_spaces(pos) + if pos > 0 and pos < line.value.length then + var c = line.value[pos] + if c == '\"' or c == '\'' or c == '(' then + pos += 1 + if c == '(' then + pos = md.read_until(comment, pos, ')') + else + pos = md.read_until(comment, pos, c) + end + if pos > 0 then is_link_ref = true + end + else + is_link_ref = true + end + end + end + end + end + if is_link_ref and not id.is_empty and not link.is_empty then + var lr = new LinkRef.with_title(link.write_to_string, comment.write_to_string) + add_link_ref(id.write_to_string, lr) + if comment.is_empty then last_link_ref = lr + return true + else + comment = new FlatBuffer + if not line.is_empty and last_link_ref != null then + pos = line.leading + var c = line.value[pos] + if c == '\"' or c == '\'' or c == '(' then + pos += 1 + if c == '(' then + pos = md.read_until(comment, pos, ')') + else + pos = md.read_until(comment, pos, c) + end + end + if not comment.is_empty then last_link_ref.title = comment.write_to_string + end + if comment.is_empty then return false + return true + end + end + + # Known link refs + # This list will be needed during output to expand links. + var link_refs: Map[String, LinkRef] = new HashMap[String, LinkRef] + + # Last encountered link ref (for multiline definitions) + # + # Markdown allows link refs to be defined over two lines: + # + # [id]: http://example.com/longish/path/to/resource/here + # "Optional Title Here" + # + private var last_link_ref: nullable LinkRef = null + + # Add a link ref to the list + fun add_link_ref(key: String, ref: LinkRef) do link_refs[key.to_lower] = ref + + # Recursively split a `block`. + # + # The block is splitted according to the type of lines it contains. + # Some blocks can be splited again recursively like lists. + # The `in_list` mode is used to recurse on list and build + # nested paragraphs or code blocks. + fun recurse(root: MDBlock, in_list: Bool) do + var old_mode = self.in_list + var old_root = self.current_block + self.in_list = in_list + + var line = root.first_line + while line != null and line.is_empty do + line = line.next + if line == null then return + end + + current_line = line + current_block = root + while current_line != null do + current_line.kind(self).process(self) + end + self.in_list = old_mode + self.current_block = old_root + end + + # Currently processed line. + # Used when visiting blocks with `recurse`. + var current_line: nullable MDLine = null is writable + + # Currently processed block. + # Used when visiting blocks with `recurse`. + var current_block: nullable MDBlock = null is writable + + # Is the current recursion in list mode? + # Used when visiting blocks with `recurse` + private var in_list = false +end + +# Emit output corresponding to blocks content. +# +# Blocks are created by a previous pass in `MarkdownProcessor`. +# The emitter use a `Decorator` to select the output format. +class MarkdownEmitter + + # Processor containing link refs. + var processor: MarkdownProcessor + + # Decorator used for output. + # Default is `HTMLDecorator` + var decorator: Decorator = new HTMLDecorator is writable + + # Create a new `MardownEmitter` using the default `HTMLDecorator` + init(processor: MarkdownProcessor) do + self.processor = processor + end + + # Create a new `MarkdownEmitter` using a custom `decorator`. + init with_decorator(processor: MarkdownProcessor, decorator: Decorator) do + init processor + self.decorator = decorator + end + + # Output `block` using `decorator` in the current buffer. + fun emit(block: Block): Text do + var buffer = push_buffer + block.emit(self) + pop_buffer + return buffer + end + + # Output the content of `block`. + 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 + + # Transform and emit mardown text starting at `from` and + # until a token with the same type as `token` is found. + # Go until the end of text if `token` is null. + fun emit_text_until(text: Text, start: Int, token: nullable Token): Int do + var old_text = current_text + var old_pos = current_pos + current_text = text + current_pos = start + while current_pos < text.length do + var mt = text.token_at(current_pos) + if (token != null and not token isa TokenNone) and + (mt.is_same_type(token) or + (token isa TokenEmStar and mt isa TokenStrongStar) or + (token isa TokenEmUnderscore and mt isa TokenStrongUnderscore)) then + return current_pos + end + mt.emit(self) + current_pos += 1 + end + current_text = old_text + current_pos = old_pos + return -1 + end + + # Currently processed position in `current_text`. + # Used when visiting inline production with `emit_text_until`. + private var current_pos: Int = -1 + + # Currently processed text. + # Used when visiting inline production with `emit_text_until`. + private var current_text: nullable Text = null + + # Stacked buffers. + private var buffer_stack = new List[FlatBuffer] + + # Push a new buffer on the stack. + private fun push_buffer: FlatBuffer do + var buffer = new FlatBuffer + buffer_stack.add buffer + return buffer + end + + # Pop the last buffer. + private fun pop_buffer do buffer_stack.pop + + # Current output buffer. + private fun current_buffer: FlatBuffer do + assert not buffer_stack.is_empty + return buffer_stack.last + end + + # Append `e` to current buffer. + fun add(e: Streamable) do + if e isa Text then + current_buffer.append e + else + current_buffer.append e.write_to_string + end + end + + # Append `c` to current buffer. + fun addc(c: Char) do current_buffer.add c + + # Append a "\n" line break. + fun addn do current_buffer.add '\n' +end + +# A Link Reference. +# Links that are specified somewhere in the mardown document to be reused as shortcuts. +# +# Example: +# +# [1]: http://example.com/ "Optional title" +class LinkRef + + # Link href + var link: String + + # Optional link title + var title: nullable String = null + + # Is the link an abreviation? + var is_abbrev = false + + init with_title(link: String, title: nullable String) do + self.link = link + self.title = title + end +end + +# A `Decorator` is used to emit mardown into a specific format. +# Default decorator used is `HTMLDecorator`. +interface Decorator + + # Render a ruler block. + fun add_ruler(v: MarkdownEmitter, block: BlockRuler) is abstract + + # Render a headline block with corresponding level. + fun add_headline(v: MarkdownEmitter, block: BlockHeadline) is abstract + + # Render a paragraph block. + fun add_paragraph(v: MarkdownEmitter, block: BlockParagraph) is abstract + + # Render a code or fence block. + fun add_code(v: MarkdownEmitter, block: BlockCode) is abstract + + # Render a blockquote. + fun add_blockquote(v: MarkdownEmitter, block: BlockQuote) is abstract + + # Render an unordered list. + fun add_unorderedlist(v: MarkdownEmitter, block: BlockUnorderedList) is abstract + + # Render an ordered list. + fun add_orderedlist(v: MarkdownEmitter, block: BlockOrderedList) is abstract + + # Render a list item. + fun add_listitem(v: MarkdownEmitter, block: BlockListItem) is abstract + + # Render an emphasis text. + fun add_em(v: MarkdownEmitter, text: Text) is abstract + + # Render a strong text. + fun add_strong(v: MarkdownEmitter, text: Text) is abstract + + # Render a super text. + fun add_super(v: MarkdownEmitter, text: Text) is abstract + + # Render a link. + fun add_link(v: MarkdownEmitter, 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 + + # Render an abbreviation. + fun add_abbr(v: MarkdownEmitter, 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 + + # Render a text and escape it. + fun append_value(v: MarkdownEmitter, value: Text) is abstract + + # Render code text from buffer and escape it. + fun append_code(v: MarkdownEmitter, buffer: Text, from, to: Int) is abstract + + # Render a character escape. + fun escape_char(v: MarkdownEmitter, char: Char) is abstract + + # Render a line break + fun add_line_break(v: MarkdownEmitter) is abstract +end + +# `Decorator` that outputs HTML. +class HTMLDecorator + super Decorator + + redef fun add_ruler(v, block) do v.add "
\n" + + redef fun add_headline(v, block) do + v.add "" + v.emit_in block + v.add "\n" + end + + redef fun add_paragraph(v, block) do + v.add "

" + v.emit_in block + v.add "

\n" + end + + redef fun add_code(v, block) do + v.add "
"
+		v.emit_in block
+		v.add "
\n" + end + + redef fun add_blockquote(v, block) do + v.add "
\n" + v.emit_in block + v.add "
\n" + end + + redef fun add_unorderedlist(v, block) do + v.add "
    \n" + v.emit_in block + v.add "
\n" + end + + redef fun add_orderedlist(v, block) do + v.add "
    \n" + v.emit_in block + v.add "
\n" + end + + redef fun add_listitem(v, block) do + v.add "
  • " + v.emit_in block + v.add "
  • \n" + 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_super(v, text) do + v.add "" + v.add text + v.add "" + end + + redef fun add_image(v, link, name, comment) do + v.add "\""" + end + + redef fun add_link(v, link, name, comment) do + v.add "" + v.emit_text(name) + v.add "" + end + + redef fun add_abbr(v, name, comment) do + v.add "" + v.emit_text(name) + v.add "" + 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 "
    " + end + + redef fun append_value(v, text) do for c in text do escape_char(v, c) + + redef fun escape_char(v, c) do + if c == '&' then + v.add "&" + else if c == '<' then + v.add "<" + else if c == '>' then + v.add ">" + else if c == '"' then + v.add """ + else if c == '\'' then + v.add "'" + else + v.addc c + end + end + + redef fun append_code(v, buffer, from, to) do + for i in [from..to[ do + var c = buffer[i] + if c == '&' then + v.add "&" + else if c == '<' then + v.add "<" + else if c == '>' then + v.add ">" + else + v.addc c + end + end + end +end + +# A block of markdown lines. +# A `MDBlock` can contains lines and/or sub-blocks. +class MDBlock + # Kind of block. + # See `Block`. + var kind: Block = new BlockNone(self) is writable + + # First line if any. + var first_line: nullable MDLine = null is writable + + # Last line if any. + var last_line: nullable MDLine = null is writable + + # First sub-block if any. + var first_block: nullable MDBlock = null is writable + + # Last sub-block if any. + var last_block: nullable MDBlock = null is writable + + # Previous block if any. + var prev: nullable MDBlock = null is writable + + # Next block if any. + var next: nullable MDBlock = null is writable + + # Does this block contain subblocks? + fun has_blocks: Bool do return first_block != null + + # Count sub-blocks. + fun count_blocks: Int do + var count = 0 + var block = first_block + while block != null do + count += 1 + block = block.next + end + return count + end + + # Does this block contain lines? + fun has_lines: Bool do return first_line != null + + # Count block lines. + fun count_lines: Int do + var count = 0 + var line = first_line + while line != null do + count += 1 + line = line.next + end + return count + end + + # Split `self` creating a new sub-block having `line` has `last_line`. + fun split(line: MDLine): MDBlock do + var block = new MDBlock + block.first_line = first_line + block.last_line = line + first_line = line.next + line.next = null + if first_line == null then + last_line = null + else + first_line.prev = null + end + if first_block == null then + first_block = block + last_block = block + else + last_block.next = block + last_block = block + end + return block + end + + # Add a `line` to this block. + fun add_line(line: MDLine) do + if last_line == null then + first_line = line + last_line = line + else + last_line.next_empty = line.is_empty + line.prev_empty = last_line.is_empty + line.prev = last_line + last_line.next = line + last_line = line + end + end + + # Remove `line` from this block. + fun remove_line(line: MDLine) do + if line.prev == null then + first_line = line.next + else + line.prev.next = line.next + end + if line.next == null then + last_line = line.prev + else + line.next.prev = line.prev + end + line.prev = null + line.next = null + end + + # Remove leading empty lines. + fun remove_leading_empty_lines: Bool do + var was_empty = false + var line = first_line + while line != null and line.is_empty do + remove_line line + line = first_line + was_empty = true + end + return was_empty + end + + # Remove trailing empty lines. + fun remove_trailing_empty_lines: Bool do + var was_empty = false + var line = last_line + while line != null and line.is_empty do + remove_line line + line = last_line + was_empty = true + end + return was_empty + end + + # Remove leading and trailing empty lines. + fun remove_surrounding_empty_lines: Bool do + var was_empty = false + if remove_leading_empty_lines then was_empty = true + if remove_trailing_empty_lines then was_empty = true + return was_empty + end + + # Remove list markers and up to 4 leading spaces. + # Used to clean nested lists. + fun remove_list_indent(v: MarkdownProcessor) do + var line = first_line + while line != null do + if not line.is_empty then + var kind = line.kind(v) + if kind isa LineList then + line.value = kind.extract_value(line) + else + line.value = line.value.substring_from(line.leading.min(4)) + end + line.leading = line.process_leading + end + line = line.next + end + end + + # Collect block line text. + fun text: String do + var text = new FlatBuffer + var line = first_line + while line != null do + if not line.is_empty then + text.append line.text + end + text.append "\n" + line = line.next + end + return text.write_to_string + end +end + +# Representation of a markdown block in the AST. +# Each `Block` is linked to a `MDBlock` that contains mardown code. +abstract class Block + + # The markdown block `self` is related to. + var block: MDBlock + + # Output `self` using `v.decorator`. + fun emit(v: MarkdownEmitter) do v.emit_in(self) + + # Emit the containts of `self`, lines or blocks. + fun emit_in(v: MarkdownEmitter) do + block.remove_surrounding_empty_lines + if block.has_lines then + emit_lines(v) + else + emit_blocks(v) + end + end + + # Emit lines contained in `block`. + fun emit_lines(v: MarkdownEmitter) do + var tpl = v.push_buffer + var line = block.first_line + while line != null do + if not line.is_empty then + v.add line.value.substring(line.leading, line.value.length - line.trailing) + if line.trailing >= 2 then v.decorator.add_line_break(v) + end + if line.next != null then + v.addn + end + line = line.next + end + v.pop_buffer + v.emit_text(tpl) + end + + # Emit sub-blocks contained in `block`. + fun emit_blocks(v: MarkdownEmitter) do + var block = self.block.first_block + while block != null do + block.kind.emit(v) + block = block.next + end + end +end + +# A block without any markdown specificities. +# +# Actually use the same implementation than `BlockCode`, +# this class is only used for typing purposes. +class BlockNone + super Block +end + +# A markdown blockquote. +class BlockQuote + super Block + + redef fun emit(v) do v.decorator.add_blockquote(v, self) + + # Remove blockquote markers. + private fun remove_block_quote_prefix(block: MDBlock) do + var line = block.first_line + while line != null do + if not line.is_empty then + if line.value[line.leading] == '>' then + var rem = line.leading + 1 + if line.leading + 1 < line.value.length and + line.value[line.leading + 1] == ' ' then + rem += 1 + end + line.value = line.value.substring_from(rem) + line.leading = line.process_leading + end + end + line = line.next + end + end +end + +# A markdown code block. +class BlockCode + super Block + + redef fun emit(v) do v.decorator.add_code(v, self) + + redef fun emit_lines(v) do + var line = block.first_line + while line != null do + if not line.is_empty then + v.decorator.append_code(v, line.value, 4, line.value.length) + end + v.addn + line = line.next + end + end +end + +# A markdown code-fence block. +# +# Actually use the same implementation than `BlockCode`, +# this class is only used for typing purposes. +class BlockFence + super BlockCode +end + +# A markdown headline. +class BlockHeadline + super Block + + redef fun emit(v) do v.decorator.add_headline(v, self) + + # Depth of the headline used to determine the headline level. + var depth = 0 + + # Remove healine marks from lines contained in `self`. + private fun transform_headline(block: MDBlock) do + if depth > 0 then return + var level = 0 + var line = block.first_line + if line.is_empty then return + var start = line.leading + while start < line.value.length and line.value[start] == '#' do + level += 1 + start += 1 + end + while start < line.value.length and line.value[start] == ' ' do + start += 1 + end + if start >= line.value.length then + line.is_empty = true + else + var nend = line.value.length - line.trailing - 1 + while line.value[nend] == '#' do nend -= 1 + while line.value[nend] == ' ' do nend -= 1 + line.value = line.value.substring(start, nend - start + 1) + line.leading = 0 + line.trailing = 0 + end + depth = level.min(6) + end +end + +# A markdown list item block. +class BlockListItem + super Block + + redef fun emit(v) do v.decorator.add_listitem(v, self) +end + +# A markdown list block. +# Can be either an ordered or unordered list, this class is mainly used to factorize code. +abstract class BlockList + super Block + + # Split list block into list items sub-blocks. + private fun init_block(v: MarkdownProcessor) do + var line = block.first_line + line = line.next + while line != null do + var t = line.kind(v) + if t isa LineList or + (not line.is_empty and (line.prev_empty and line.leading == 0 and + not (t isa LineList))) then + var sblock = block.split(line.prev.as(not null)) + sblock.kind = new BlockListItem(sblock) + end + line = line.next + end + var sblock = block.split(block.last_line.as(not null)) + sblock.kind = new BlockListItem(sblock) + end + + # Expand list items as paragraphs if needed. + private fun expand_paragraphs(block: MDBlock) do + var outer = block.first_block + var inner: nullable MDBlock + var has_paragraph = false + while outer != null and not has_paragraph do + if outer.kind isa BlockListItem then + inner = outer.first_block + while inner != null and not has_paragraph do + if inner.kind isa BlockParagraph then + has_paragraph = true + end + inner = inner.next + end + end + outer = outer.next + end + if has_paragraph then + outer = block.first_block + while outer != null do + if outer.kind isa BlockListItem then + inner = outer.first_block + while inner != null do + if inner.kind isa BlockNone then + inner.kind = new BlockParagraph(inner) + end + inner = inner.next + end + end + outer = outer.next + end + end + end +end + +# A markdown ordered list. +class BlockOrderedList + super BlockList + + redef fun emit(v) do v.decorator.add_orderedlist(v, self) +end + +# A markdown unordred list. +class BlockUnorderedList + super BlockList + + redef fun emit(v) do v.decorator.add_unorderedlist(v, self) +end + +# A markdown paragraph block. +class BlockParagraph + super Block + + redef fun emit(v) do v.decorator.add_paragraph(v, self) +end + +# A markdown ruler. +class BlockRuler + super Block + + redef fun emit(v) do v.decorator.add_ruler(v, self) +end + +# Xml blocks that can be found in markdown markup. +class BlockXML + super Block + + redef fun emit_lines(v) do + var line = block.first_line + while line != null do + if not line.is_empty then v.add line.value + v.addn + line = line.next + end + end +end + +# A markdown line. +class MDLine + + # Text contained in this line. + var value: String is writable + + # Is this line empty? + # Lines containing only spaces are considered empty. + var is_empty: Bool = true is writable + + # Previous line in `MDBlock` or null if first line. + var prev: nullable MDLine = null is writable + + # Next line in `MDBlock` or null if last line. + var next: nullable MDLine = null is writable + + # Is the previous line empty? + var prev_empty: Bool = false is writable + + # Is the next line empty? + var next_empty: Bool = false is writable + + init(value: String) do + self.value = value + self.leading = process_leading + if leading != value.length then + self.is_empty = false + self.trailing = process_trailing + end + end + + # Set `value` as an empty String and update `leading`, `trailing` and is_`empty`. + fun clear do + value = "" + leading = 0 + trailing = 0 + is_empty = true + if prev != null then prev.next_empty = true + if next != null then next.prev_empty = true + end + + # The type of line. + # see `md_line_*` + fun kind(v: MarkdownProcessor): Line do + var value = self.value + if is_empty then return new LineEmpty + if leading > 3 then return new LineCode + if value[leading] == '#' then return new LineHeadline + if value[leading] == '>' then return new LineBlockquote + + if value.length - leading - trailing > 2 then + if value[leading] == '`' and count_chars_start('`') >= 3 then + return new LineFence + end + if value[leading] == '~' and count_chars_start('~') >= 3 then + return new LineFence + end + end + + if value.length - leading - trailing > 2 and + (value[leading] == '*' or value[leading] == '-' or value[leading] == '_') then + if count_chars(value[leading]) >= 3 then + return new LineHR + end + end + + if value.length - leading >= 2 and value[leading + 1] == ' ' then + var c = value[leading] + if c == '*' or c == '-' or c == '+' then return new LineUList + end + + if value.length - leading >= 3 and value[leading].is_digit then + var i = leading + 1 + while i < value.length and value[i].is_digit do i += 1 + if i + 1 < value.length and value[i] == '.' and value[i + 1] == ' ' then + return new LineOList + end + end + + if value[leading] == '<' and check_html then return new LineXML + + if next != null and not next.is_empty then + if next.count_chars('=') > 0 then + return new LineHeadline1 + end + if next.count_chars('-') > 0 then + return new LineHeadline2 + end + end + return new LineOther + end + + # Number or leading spaces on this line. + var leading: Int = 0 is writable + + # Compute `leading` depending on `value`. + fun process_leading: Int do + var count = 0 + var value = self.value + while count < value.length and value[count] == ' ' do count += 1 + if leading == value.length then clear + return count + end + + # Number of trailing spaces on this line. + var trailing: Int = 0 is writable + + # Compute `trailing` depending on `value`. + fun process_trailing: Int do + var count = 0 + var value = self.value + while value[value.length - count - 1] == ' ' do + count += 1 + end + return count + end + + # Count the amount of `ch` in this line. + # Return A value > 0 if this line only consists of `ch` end spaces. + fun count_chars(ch: Char): Int do + var count = 0 + for c in value do + if c == ' ' then + continue + end + if c == ch then + count += 1 + continue + end + count = 0 + break + end + return count + end + + # Count the amount of `ch` at the start of this line ignoring spaces. + fun count_chars_start(ch: Char): Int do + var count = 0 + for c in value do + if c == ' ' then + continue + end + if c == ch then + count += 1 + else + break + end + end + return count + end + + # Last XML line if any. + private var xml_end_line: nullable MDLine = null + + # Does `value` contains valid XML markup? + private fun check_html: Bool do + var tags = new Array[String] + var tmp = new FlatBuffer + var pos = leading + if pos + 1 < value.length and value[pos + 1] == '!' then + if read_xml_comment(self, pos) > 0 then return true + end + pos = value.read_xml(tmp, pos, false) + var tag: String + if pos > -1 then + tag = tmp.xml_tag + if not tag.is_html_block then + return false + end + if tag == "hr" then + xml_end_line = self + return true + end + tags.add tag + var line: nullable MDLine = self + while line != null do + while pos < line.value.length and line.value[pos] != '<' do + pos += 1 + end + if pos >= line.value.length then + if line.value[pos - 2] == '/' then + tags.pop + if tags.is_empty then + xml_end_line = line + break + end + end + line = line.next + pos = 0 + else + tmp = new FlatBuffer + var new_pos = line.value.read_xml(tmp, pos, false) + if new_pos > 0 then + tag = tmp.xml_tag + if tag.is_html_block and not tag == "hr" then + if tmp[1] == '/' then + if tags.last != tag then + return false + end + tags.pop + else + tags.add tag + end + end + if tags.is_empty then + xml_end_line = line + break + end + pos = new_pos + else + pos += 1 + end + end + end + return tags.is_empty + end + return false + end + + # Read a XML comment. + # Used by `check_html`. + private fun read_xml_comment(first_line: MDLine, start: Int): Int do + var line: nullable MDLine = first_line + if start + 3 < line.value.length then + if line.value[2] == '-' and line.value[3] == '-' then + var pos = start + 4 + while line != null do + while pos < line.value.length and line.value[pos] != '-' do + pos += 1 + end + if pos == line.value.length then + line = line.next + pos = 0 + else + if pos + 2 < line.value.length then + if line.value[pos + 1] == '-' and line.value[pos + 2] == '>' then + first_line.xml_end_line = line + return pos + 3 + end + end + pos += 1 + end + end + end + end + return -1 + end + + # Extract the text of `self` without leading and trailing. + fun text: String do return value.substring(leading, value.length - trailing) +end + +# A markdown line. +interface Line + + # Parse the line. + # See `MarkdownProcessor::recurse`. + fun process(v: MarkdownProcessor) is abstract +end + +# An empty markdown line. +class LineEmpty + super Line + + redef fun process(v) do + v.current_line = v.current_line.next + end +end + +# A non-specific markdown construction. +# Mainly used as part of another line construct such as paragraphs or lists. +class LineOther + super Line + + redef fun process(v) do + var line = v.current_line + # go to block end + var was_empty = line.prev_empty + while line != null and not line.is_empty do + var t = line.kind(v) + if v.in_list and t isa LineList then + break + end + if t isa LineCode or t isa LineFence then + break + end + if t isa LineHeadline or t isa LineHeadline1 or t isa LineHeadline2 or + t isa LineHR or t isa LineBlockquote or t isa LineXML then + break + end + line = line.next + end + # build block + var bk: Block + if line != null and not line.is_empty then + var block = v.current_block.split(line.prev.as(not null)) + if v.in_list and not was_empty then + block.kind = new BlockNone(block) + else + block.kind = new BlockParagraph(block) + end + v.current_block.remove_leading_empty_lines + else + var block: MDBlock + if line != null then + block = v.current_block.split(line) + else + block = v.current_block.split(v.current_block.last_line.as(not null)) + end + if v.in_list and (line == null or not line.is_empty) and not was_empty then + block.kind = new BlockNone(block) + else + block.kind = new BlockParagraph(block) + end + v.current_block.remove_leading_empty_lines + end + v.current_line = v.current_block.first_line + end +end + +# A line of markdown code. +class LineCode + super Line + + redef fun process(v) do + var line = v.current_line + # lookup block end + while line != null and (line.is_empty or line.kind(v) isa LineCode) do + line = line.next + end + # split at block end line + var block: MDBlock + if line != null then + block = v.current_block.split(line.prev.as(not null)) + else + block = v.current_block.split(v.current_block.last_line.as(not null)) + end + block.kind = new BlockCode(block) + block.remove_surrounding_empty_lines + v.current_line = v.current_block.first_line + end +end + +# A line of raw XML. +class LineXML + super Line + + redef fun process(v) do + var line = v.current_line + var prev = line.prev + if prev != null then v.current_block.split(prev) + var block = v.current_block.split(line.xml_end_line.as(not null)) + block.kind = new BlockXML(block) + v.current_block.remove_leading_empty_lines + v.current_line = v.current_block.first_line + end +end + +# A markdown blockquote line. +class LineBlockquote + super Line + + redef fun process(v) do + var line = v.current_line + # go to bquote end + while line != null do + if not line.is_empty and (line.prev_empty and + line.leading == 0 and + not line.kind(v) isa LineBlockquote) then break + line = line.next + end + # build sub block + var block: MDBlock + if line != null then + block = v.current_block.split(line.prev.as(not null)) + else + block = v.current_block.split(v.current_block.last_line.as(not null)) + end + var kind = new BlockQuote(block) + block.kind = kind + block.remove_surrounding_empty_lines + kind.remove_block_quote_prefix(block) + v.current_line = line + v.recurse(block, false) + v.current_line = v.current_block.first_line + end +end + +# A markdown ruler line. +class LineHR + super Line + + redef fun process(v) do + var line = v.current_line + if line.prev != null then v.current_block.split(line.prev.as(not null)) + var block = v.current_block.split(line.as(not null)) + block.kind = new BlockRuler(block) + v.current_block.remove_leading_empty_lines + v.current_line = v.current_block.first_line + end +end + +# A markdown fence code line. +class LineFence + super Line + + redef fun process(v) do + # go to fence end + var line = v.current_line.next + while line != null do + if line.kind(v) isa LineFence then break + line = line.next + end + if line != null then + line = line.next + end + # build fence block + var block: MDBlock + if line != null then + block = v.current_block.split(line.prev.as(not null)) + else + block = v.current_block.split(v.current_block.last_line.as(not null)) + end + block.kind = new BlockFence(block) + block.first_line.clear + if block.last_line.kind(v) isa LineFence then + block.last_line.clear + end + block.remove_surrounding_empty_lines + v.current_line = line + end +end + +# A markdown headline. +class LineHeadline + super Line + + redef fun process(v) do + var line = v.current_line + var lprev = line.prev + if lprev != null then v.current_block.split(lprev) + var block = v.current_block.split(line.as(not null)) + var kind = new BlockHeadline(block) + block.kind = kind + # TODO block ID + # block.id = block.first_line.strip_id + kind.transform_headline(block) + v.current_block.remove_leading_empty_lines + v.current_line = v.current_block.first_line + end +end + +# A markdown headline of level 1. +class LineHeadline1 + super LineHeadline + + redef fun process(v) do + var line = v.current_line + var lprev = line.prev + if lprev != null then v.current_block.split(lprev) + line.next.clear + var block = v.current_block.split(line.as(not null)) + var kind = new BlockHeadline(block) + kind.depth = 1 + # TODO block ID + # block.id = block.first_line.strip_id + kind.transform_headline(block) + block.kind = kind + v.current_block.remove_leading_empty_lines + v.current_line = v.current_block.first_line + end +end + +# A markdown headline of level 2. +class LineHeadline2 + super LineHeadline + + redef fun process(v) do + var line = v.current_line + var lprev = line.prev + if lprev != null then v.current_block.split(lprev) + line.next.clear + var block = v.current_block.split(line.as(not null)) + var kind = new BlockHeadline(block) + kind.depth = 2 + # TODO block ID + # block.id = block.first_line.strip_id + kind.transform_headline(block) + block.kind = kind + v.current_block.remove_leading_empty_lines + v.current_line = v.current_block.first_line + end +end + +# A markdown list line. +# Mainly used to factorize code between ordered and unordered lists. +class LineList + super Line + + redef fun process(v) do + var line = v.current_line + # go to list end + while line != null do + var t = line.kind(v) + if not line.is_empty and (line.prev_empty and line.leading == 0 and + not t isa LineList) then break + line = line.next + end + # build list block + var list: MDBlock + if line != null then + list = v.current_block.split(line.prev.as(not null)) + else + list = v.current_block.split(v.current_block.last_line.as(not null)) + end + var kind = block_kind(list) + list.kind = kind + list.first_line.prev_empty = false + list.last_line.next_empty = false + list.remove_surrounding_empty_lines + list.first_line.prev_empty = false + list.last_line.next_empty = false + kind.init_block(v) + var block = list.first_block + while block != null do + block.remove_list_indent(v) + v.recurse(block, true) + block = block.next + end + kind.expand_paragraphs(list) + v.current_line = line + end + + # Create a new block kind based on this line. + protected fun block_kind(block: MDBlock): BlockList is abstract + + protected fun extract_value(line: MDLine): String is abstract +end + +# An ordered list line. +class LineOList + super LineList + + redef fun block_kind(block) do return new BlockOrderedList(block) + + redef fun extract_value(line) do + return line.value.substring_from(line.value.index_of('.') + 2) + end +end + +# An unordered list line. +class LineUList + super LineList + + redef fun block_kind(block) do return new BlockUnorderedList(block) + + redef fun extract_value(line) do + return line.value.substring_from(line.leading + 2) + end +end + +# A token represent a character in the markdown input. +# Some tokens have a specific markup behaviour that is handled here. +abstract class Token + + # Position of `self` in markdown input. + var pos: Int + + # Character found at `pos` in the markdown input. + var char: Char + + # Output that token using `MarkdownEmitter::decorator`. + fun emit(v: MarkdownEmitter) do v.addc char +end + +# A token without a specific meaning. +class TokenNone + super Token +end + +# An emphasis token. +abstract class TokenEm + super Token + + redef fun emit(v) do + var tmp = v.push_buffer + var b = v.emit_text_until(v.current_text.as(not null), pos + 1, self) + v.pop_buffer + if b > 0 then + v.decorator.add_em(v, tmp) + v.current_pos = b + else + v.addc char + end + end +end + +# An emphasis star token. +class TokenEmStar + super TokenEm +end + +# An emphasis underscore token. +class TokenEmUnderscore + super TokenEm +end + +# A strong token. +abstract class TokenStrong + super Token + + redef fun emit(v) do + var tmp = v.push_buffer + var b = v.emit_text_until(v.current_text.as(not null), pos + 2, self) + v.pop_buffer + if b > 0 then + v.decorator.add_strong(v, tmp) + v.current_pos = b + 1 + else + v.addc char + end + end +end + +# A strong star token. +class TokenStrongStar + super TokenStrong +end + +# A strong underscore token. +class TokenStrongUnderscore + super TokenStrong +end + +# A code token. +# This class is mainly used to factorize work between single and double quoted span codes. +abstract class TokenCode + super Token + + redef fun emit(v) do + var a = pos + next_pos + 1 + var b = v.current_text.find_token(a, self) + if b > 0 then + v.current_pos = b + next_pos + while a < b and v.current_text[a] == ' ' do a += 1 + if a < b then + while v.current_text[b - 1] == ' ' do b -= 1 + v.decorator.add_span_code(v, v.current_text.as(not null), a, b) + end + else + v.addc char + end + end + + private fun next_pos: Int is abstract +end + +# A span code token. +class TokenCodeSingle + super TokenCode + + redef fun next_pos do return 0 +end + +# A doubled span code token. +class TokenCodeDouble + super TokenCode + + redef fun next_pos do return 1 +end + +# A link or image token. +# This class is mainly used to factorize work between images and links. +abstract class TokenLinkOrImage + super Token + + # Link adress + var link: nullable Text = null + + # Link text + var name: nullable Text = null + + # Link title + var comment: nullable Text = null + + # Is the link construct an abbreviation? + var is_abbrev = false + + redef fun emit(v) do + var tmp = new FlatBuffer + var b = check_link(v, tmp, pos, self) + if b > 0 then + emit_hyper(v) + v.current_pos = b + else + v.addc char + end + end + + # Emit the hyperlink as link or image. + private fun emit_hyper(v: MarkdownEmitter) is abstract + + # Check if the link is a valid link. + private fun check_link(v: MarkdownEmitter, out: FlatBuffer, start: Int, token: Token): Int do + var md = v.current_text + var pos + if token isa TokenLink then + pos = start + 1 + else + pos = start + 2 + end + var tmp = new FlatBuffer + pos = md.read_md_link_id(tmp, pos) + if pos < start then return -1 + name = tmp + var old_pos = pos + pos += 1 + pos = md.skip_spaces(pos) + if pos < start then + var tid = name.write_to_string.to_lower + if v.processor.link_refs.has_key(tid) then + var lr = v.processor.link_refs[tid] + is_abbrev = lr.is_abbrev + link = lr.link + comment = lr.title + pos = old_pos + else + return -1 + end + else if md[pos] == '(' then + pos += 1 + pos = md.skip_spaces(pos) + if pos < start then return -1 + tmp = new FlatBuffer + var use_lt = md[pos] == '<' + if use_lt then + pos = md.read_until(tmp, pos + 1, '>') + else + pos = md.read_md_link(tmp, pos) + end + if pos < start then return -1 + if use_lt then pos += 1 + link = tmp.write_to_string + if md[pos] == ' ' then + pos = md.skip_spaces(pos) + if pos > start and md[pos] == '"' then + pos += 1 + tmp = new FlatBuffer + pos = md.read_until(tmp, pos, '"') + if pos < start then return -1 + comment = tmp.write_to_string + pos += 1 + pos = md.skip_spaces(pos) + if pos == -1 then return -1 + end + end + if md[pos] != ')' then return -1 + else if md[pos] == '[' then + pos += 1 + tmp = new FlatBuffer + pos = md.read_raw_until(tmp, pos, ']') + if pos < start then return -1 + var id + if tmp.length > 0 then + id = tmp + else + id = name + end + var tid = id.write_to_string.to_lower + if v.processor.link_refs.has_key(tid) then + var lr = v.processor.link_refs[tid] + link = lr.link + comment = lr.title + end + else + var tid = name.write_to_string.replace("\n", " ").to_lower + if v.processor.link_refs.has_key(tid) then + var lr = v.processor.link_refs[tid] + link = lr.link + comment = lr.title + pos = old_pos + else + return -1 + end + end + if link == null then return -1 + return pos + end +end + +# A markdown link token. +class TokenLink + super TokenLinkOrImage + + redef fun emit_hyper(v) do + if is_abbrev and comment != null then + v.decorator.add_abbr(v, name.as(not null), comment.as(not null)) + else + v.decorator.add_link(v, link.as(not null), name.as(not null), comment) + end + end +end + +# A markdown image token. +class TokenImage + super TokenLinkOrImage + + redef fun emit_hyper(v) do + v.decorator.add_image(v, link.as(not null), name.as(not null), comment) + end +end + +# A HTML/XML token. +class TokenHTML + super Token + + redef fun emit(v) do + var tmp = new FlatBuffer + var b = check_html(v, tmp, v.current_text.as(not null), v.current_pos) + if b > 0 then + v.add tmp + v.current_pos = b + else + v.decorator.escape_char(v, char) + end + end + + # Is the HTML valid? + # Also take care of link and mailto shortcuts. + private fun check_html(v: MarkdownEmitter, out: FlatBuffer, md: Text, start: Int): Int do + # check for auto links + var tmp = new FlatBuffer + var pos = md.read_until(tmp, start + 1, ':', ' ', '>', '\n') + if pos != -1 and md[pos] == ':' and tmp.is_link_prefix then + pos = md.read_until(tmp, pos, '>') + if pos != -1 then + var link = tmp.write_to_string + v.decorator.add_link(v, link, link, null) + return pos + end + end + # TODO check for mailto + # check for inline html + if start + 2 < md.length then + return md.read_xml(out, start, true) + end + return -1 + end +end + +# An HTML entity token. +class TokenEntity + super Token + + redef fun emit(v) do + var tmp = new FlatBuffer + var b = check_entity(tmp, v.current_text.as(not null), pos) + if b > 0 then + v.add tmp + v.current_pos = b + else + v.decorator.escape_char(v, char) + end + end + + # Is the entity valid? + private fun check_entity(out: FlatBuffer, md: Text, start: Int): Int do + var pos = md.read_until(out, start, ';') + if pos < 0 or out.length < 3 then + return -1 + end + if out[1] == '#' then + if out[2] == 'x' or out[2] == 'X' then + if out.length < 4 then return -1 + for i in [3..out.length[ do + var c = out[i] + if (c < '0' or c > '9') and (c < 'a' and c > 'f') and (c < 'A' and c > 'F') then + return -1 + end + end + else + for i in [2..out.length[ do + var c = out[i] + if c < '0' or c > '9' then return -1 + end + end + out.add ';' + else + for i in [1..out.length[ do + var c = out[i] + if not c.is_digit and not c.is_letter then return -1 + end + out.add ';' + # TODO check entity is valid + # if out.is_entity then + return pos + # else + # return -1 + # end + end + return pos + end +end + +# A markdown escape token. +class TokenEscape + super Token + + redef fun emit(v) do + v.current_pos += 1 + v.addc v.current_text[v.current_pos] + end +end + +# A markdown super token. +class TokenSuper + super Token + + redef fun emit(v) do + var tmp = v.push_buffer + var b = v.emit_text_until(v.current_text.as(not null), pos + 1, self) + v.pop_buffer + if b > 0 then + v.decorator.add_super(v, tmp) + v.current_pos = b + else + v.addc char + end + end +end + +redef class Text + + # Get the token kind at `pos`. + private fun token_at(pos: Int): Token do + var c0: Char + var c1: Char + var c2: Char + var c3: Char + + if pos > 0 then + c0 = self[pos - 1] + else + c0 = ' ' + end + var c = self[pos] + + if pos + 1 < length then + c1 = self[pos + 1] + else + c1 = ' ' + end + if pos + 2 < length then + c2 = self[pos + 2] + else + c2 = ' ' + end + if pos + 3 < length then + c3 = self[pos + 3] + else + c3 = ' ' + end + + if c == '*' then + if c1 == '*' then + if c0 != ' ' or c2 != ' ' then + return new TokenStrongStar(pos, c) + else + return new TokenEmStar(pos, c) + end + end + if c0 != ' ' or c1 != ' ' then + return new TokenEmStar(pos, c) + else + return new TokenNone(pos, c) + end + else if c == '_' then + if c1 == '_' then + if c0 != ' ' or c2 != ' 'then + return new TokenStrongUnderscore(pos, c) + else + return new TokenEmUnderscore(pos, c) + end + end + if c0 != ' ' or c1 != ' ' then + return new TokenEmUnderscore(pos, c) + else + return new TokenNone(pos, c) + end + else if c == '!' then + if c1 == '[' then return new TokenImage(pos, c) + return new TokenNone(pos, c) + else if c == '[' then + return new TokenLink(pos, c) + else if c == ']' then + return new TokenNone(pos, c) + else if c == '`' then + if c1 == '`' then + return new TokenCodeDouble(pos, c) + else + return new TokenCodeSingle(pos, c) + end + else if c == '\\' then + if c1 == '\\' or c1 == '[' or c1 == ']' or c1 == '(' or c1 == ')' or c1 == '{' or c1 == '}' or c1 == '#' or c1 == '"' or c1 == '\'' or c1 == '.' or c1 == '<' or c1 == '>' or c1 == '*' or c1 == '+' or c1 == '-' or c1 == '_' or c1 == '!' or c1 == '`' or c1 == '~' or c1 == '^' then + return new TokenEscape(pos, c) + else + return new TokenNone(pos, c) + end + else if c == '<' then + return new TokenHTML(pos, c) + else if c == '&' then + return new TokenEntity(pos, c) + else if c == '^' then + if c0 == '^' or c1 == '^' then + return new TokenNone(pos, c) + else + return new TokenSuper(pos, c) + end + else + return new TokenNone(pos, c) + end + end + + # Find the position of a `token` in `self`. + private fun find_token(start: Int, token: Token): Int do + var pos = start + while pos < length do + if token_at(pos).is_same_type(token) then + return pos + end + pos += 1 + end + return -1 + end + + # Get the position of the next non-space character. + private fun skip_spaces(start: Int): Int do + var pos = start + while pos > -1 and pos < length and (self[pos] == ' ' or self[pos] == '\n') do + pos += 1 + end + if pos < length then return pos + return -1 + end + + # Read `self` until `nend` and append it to the `out` buffer. + # Escape markdown special chars. + private fun read_until(out: FlatBuffer, start: Int, nend: Char...): Int do + var pos = start + while pos < length do + var c = self[pos] + if c == '\\' and pos + 1 < length then + pos = escape(out, self[pos + 1], pos) + else + var end_reached = false + for n in nend do + if c == n then + end_reached = true + break + end + end + if end_reached then break + out.add c + end + pos += 1 + end + if pos == length then return -1 + return pos + end + + # Read `self` as raw text until `nend` and append it to the `out` buffer. + # No escape is made. + private fun read_raw_until(out: FlatBuffer, start: Int, nend: Char...): Int do + var pos = start + while pos < length do + var c = self[pos] + var end_reached = false + for n in nend do + if c == n then + end_reached = true + break + end + end + if end_reached then break + out.add c + pos += 1 + end + if pos == length then return -1 + return pos + end + + # Read `self` as XML until `to` and append it to the `out` buffer. + # Escape HTML special chars. + private fun read_xml_until(out: FlatBuffer, from: Int, to: Char...): Int do + var pos = from + var in_str = false + var str_char: nullable Char = null + while pos < length do + var c = self[pos] + if in_str then + if c == '\\' then + out.add c + pos += 1 + if pos < length then + out.add c + pos += 1 + end + continue + end + if c == str_char then + in_str = false + out.add c + pos += 1 + continue + end + end + if c == '"' or c == '\'' then + in_str = true + str_char = c + end + if not in_str then + var end_reached = false + for n in [0..to.length[ do + if c == to[n] then + end_reached = true + break + end + end + if end_reached then break + end + out.add c + pos += 1 + end + if pos == length then return -1 + return pos + end + + # Read `self` as XML and append it to the `out` buffer. + # Safe mode can be activated to limit reading to valid xml. + private fun read_xml(out: FlatBuffer, start: Int, safe_mode: Bool): Int do + var pos = 0 + var is_close_tag = false + if start + 1 >= length then return -1 + if self[start + 1] == '/' then + is_close_tag = true + pos = start + 2 + else if self[start + 1] == '!' then + out.append "') + if pos == -1 then return -1 + var tag = tmp.write_to_string.trim.to_lower + if tag.is_html_unsafe then + out.append "<" + if is_close_tag then out.add '/' + out.append tmp + else + out.append "<" + if is_close_tag then out.add '/' + out.append tmp + end + else + out.add '<' + if is_close_tag then out.add '/' + pos = read_xml_until(out, pos, ' ', '/', '>') + end + if pos == -1 then return -1 + pos = read_xml_until(out, pos, '/', '>') + if pos == -1 then return -1 + if self[pos] == '/' then + out.append " /" + pos = self.read_xml_until(out, pos + 1, '>') + if pos == -1 then return -1 + end + if self[pos] == '>' then + out.add '>' + return pos + end + return -1 + end + + # Read a markdown link address and append it to the `out` buffer. + private fun read_md_link(out: FlatBuffer, start: Int): Int do + var pos = start + var counter = 1 + while pos < length do + var c = self[pos] + if c == '\\' and pos + 1 < length then + pos = escape(out, self[pos + 1], pos) + else + var end_reached = false + if c == '(' then + counter += 1 + else if c == ' ' then + if counter == 1 then end_reached = true + else if c == ')' then + counter -= 1 + if counter == 0 then end_reached = true + end + if end_reached then break + out.add c + end + pos += 1 + end + if pos == length then return -1 + return pos + end + + # Read a markdown link text and append it to the `out` buffer. + private fun read_md_link_id(out: FlatBuffer, start: Int): Int do + var pos = start + var counter = 1 + while pos < length do + var c = self[pos] + var end_reached = false + if c == '[' then + counter += 1 + out.add c + else if c == ']' then + counter -= 1 + if counter == 0 then + end_reached = true + else + out.add c + end + else + out.add c + end + if end_reached then break + pos += 1 + end + if pos == length then return -1 + return pos + end + + # Extract the XML tag name from a XML tag. + private fun xml_tag: String do + var tpl = new FlatBuffer + var pos = 1 + if pos < length and self[1] == '/' then pos += 1 + while pos < length - 1 and (self[pos].is_digit or self[pos].is_letter) do + tpl.add self[pos] + pos += 1 + end + return tpl.write_to_string.to_lower + end + + # Read and escape the markdown contained in `self`. + private fun escape(out: FlatBuffer, c: Char, pos: Int): Int do + if c == '\\' or c == '[' or c == ']' or c == '(' or c == ')' or c == '{' or + c == '}' or c == '#' or c == '"' or c == '\'' or c == '.' or c == '<' or + c == '>' or c == '*' or c == '+' or c == '-' or c == '_' or c == '!' or + c == '`' or c == '~' or c == '^' then + out.add c + return pos + 1 + end + out.add '\\' + return pos + end + + # Is `self` an unsafe HTML element? + private fun is_html_unsafe: Bool do return html_unsafe_tags.has(self.write_to_string) + + # Is `self` a HRML block element? + private fun is_html_block: Bool do return html_block_tags.has(self.write_to_string) + + # Is `self` a link prefix? + private fun is_link_prefix: Bool do return html_link_prefixes.has(self.write_to_string) + + private fun html_unsafe_tags: Array[String] do return once ["applet", "head", "body", "frame", "frameset", "iframe", "script", "object"] + + private fun html_block_tags: Array[String] do return once ["address", "article", "aside", "audio", "blockquote", "canvas", "dd", "div", "dl", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "noscript", "ol", "output", "p", "pre", "section", "table", "tfoot", "ul", "video"] + + private fun html_link_prefixes: Array[String] do return once ["http", "https", "ftp", "ftps"] +end + +redef class String + + # Parse `self` as markdown and return the HTML representation + #. + # var md = "**Hello World!**" + # var html = md.md_to_html + # assert html == "

    Hello World!

    \n" + fun md_to_html: Streamable do + var processor = new MarkdownProcessor + return processor.process(self) + end +end diff --git a/lib/markdown/nitmd.nit b/lib/markdown/nitmd.nit new file mode 100644 index 0000000..781a484 --- /dev/null +++ b/lib/markdown/nitmd.nit @@ -0,0 +1,36 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A Markdown parser for Nit. +module nitmd + +import markdown + +if args.length != 1 then + print "usage: nitmd " + exit 0 +end + +var file = args.first +if not file.file_exists then + print "'{file}' not found" + exit 0 +end + +var ifs = new IFStream.open(file) +var md = ifs.read_all +ifs.close + +var processor = new MarkdownProcessor +print processor.process(md) diff --git a/lib/markdown/test_markdown.nit b/lib/markdown/test_markdown.nit new file mode 100644 index 0000000..6dba442 --- /dev/null +++ b/lib/markdown/test_markdown.nit @@ -0,0 +1,2458 @@ +# 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. + +# Test suites for module `markdown` +module test_markdown is test_suite + +import test_suite +intrude import markdown + +class TestMarkdownProcessor + super TestSuite + + fun test_process_empty do + var test = "" + var exp = "" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_tabs do + var test = """ + some code +""" + var exp = """
    some code
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + + fun test_process_par1 do + var test = "test" + var exp = "

    test

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_par2 do + var test = """ +line1 +line2 + +line3 line4 + +line5""" + var exp = """ +

    line1 +line2

    +

    line3 line4

    +

    line5

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_par3 do + var test = """ +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + +Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing. +""" + var exp = """ +

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

    +

    Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_headings_1 do + var test = """ +This is a H1 +============= + +This is a H2 +------------- +""" + var exp = "

    This is a H1

    \n

    This is a H2

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_headings_2 do + var test = """ +# This is a H1 + +## This is a H2 +###### This is a H6 +""" + var exp = "

    This is a H1

    \n

    This is a H2

    \n
    This is a H6
    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_headings_3 do + var test = """ +# This is a H1 # + +## This is a H2 ## + +### This is a H3 ###### +""" + var exp = "

    This is a H1

    \n

    This is a H2

    \n

    This is a H3

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_hr do + var test = """ +* * * + +*** + +***** + +- - - + +--------------------------------------- +""" + var exp = "
    \n
    \n
    \n
    \n
    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_bquote1 do + var test = """ +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. +> +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +> id sem consectetuer libero luctus adipiscing. +""" + var exp = """
    +

    This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

    +

    Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing.

    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_bquote2 do + var test = """ +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing. +""" + var exp = """
    +

    This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

    +

    Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing.

    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_bquote3 do + var test = """ +> This is the first level of quoting. +> +> > This is nested blockquote. +> +> Back to the first level. +""" + var exp = """
    +

    This is the first level of quoting.

    +
    +

    This is nested blockquote.

    +
    +

    Back to the first level.

    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list1 do + var test = """ +* Red +* Green +* Blue +""" + var exp = """
      +
    • Red
    • +
    • Green
    • +
    • Blue
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list2 do + var test = """ ++ Red ++ Green ++ Blue +""" + var exp = """
      +
    • Red
    • +
    • Green
    • +
    • Blue
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list3 do + var test = """ +- Red +- Green +- Blue +""" + var exp = """
      +
    • Red
    • +
    • Green
    • +
    • Blue
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list4 do + var test = """ +1. Bird +2. McHale +3. Parish +""" + var exp = """
      +
    1. Bird
    2. +
    3. McHale
    4. +
    5. Parish
    6. +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list5 do + var test = """ +3. Bird +1. McHale +8. Parish +""" + var exp = """
      +
    1. Bird
    2. +
    3. McHale
    4. +
    5. Parish
    6. +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list6 do + var test = """ +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, + viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. + Suspendisse id sem consectetuer libero luctus adipiscing. +""" + var exp = """ +
      +
    • Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, +viverra nec, fringilla in, laoreet vitae, risus.
    • +
    • Donec sit amet nisl. Aliquam semper ipsum sit amet velit. +Suspendisse id sem consectetuer libero luctus adipiscing.
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list7 do + var test = """ +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, +viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. +Suspendisse id sem consectetuer libero luctus adipiscing. +""" + var exp = """ +
      +
    • Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, +viverra nec, fringilla in, laoreet vitae, risus.
    • +
    • Donec sit amet nisl. Aliquam semper ipsum sit amet velit. +Suspendisse id sem consectetuer libero luctus adipiscing.
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list8 do + var test = """ +* Bird + +* Magic +""" + var exp = """ +
      +
    • Bird

      +
    • +
    • Magic

      +
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list9 do + var test = """ +1. This is a list item with two paragraphs. Lorem ipsum dolor + sit amet, consectetuer adipiscing elit. Aliquam hendrerit + mi posuere lectus. + + Vestibulum enim wisi, viverra nec, fringilla in, laoreet + vitae, risus. Donec sit amet nisl. Aliquam semper ipsum + sit amet velit. + +2. Suspendisse id sem consectetuer libero luctus adipiscing. +""" + var exp = """ +
      +
    1. This is a list item with two paragraphs. Lorem ipsum dolor +sit amet, consectetuer adipiscing elit. Aliquam hendrerit +mi posuere lectus.

      +

      Vestibulum enim wisi, viverra nec, fringilla in, laoreet +vitae, risus. Donec sit amet nisl. Aliquam semper ipsum +sit amet velit.

      +
    2. +
    3. Suspendisse id sem consectetuer libero luctus adipiscing.

      +
    4. +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_list10 do + var test = """ +* This is a list item with two paragraphs. + + This is the second paragraph in the list item. You're +only required to indent the first line. Lorem ipsum dolor +sit amet, consectetuer adipiscing elit. + +* Another item in the same list. +""" + var exp = """ +
      +
    • This is a list item with two paragraphs.

      +

      This is the second paragraph in the list item. You're +only required to indent the first line. Lorem ipsum dolor +sit amet, consectetuer adipiscing elit.

      +
    • +
    • Another item in the same list.

      +
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_code1 do + var test = """ +This is a normal paragraph: + + This is a code block. +""" + var exp = """

    This is a normal paragraph:

    +
    This is a code block.
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_code2 do + var test = """ +Here is an example of AppleScript: + + tell application "Foo" + beep + end tell + + +""" + var exp = """ +

    Here is an example of AppleScript:

    +
    tell application "Foo"
    +    beep
    +end tell
    +
    +<div class="footer">
    +    &copy; 2004 Foo Corporation
    +</div>
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_nesting1 do + var test = """ +> ## This is a header. +> +> 1. This is the first list item. +> 2. This is the second list item. +> +> Here's some example code: +> +> return shell_exec("echo $input | $markdown_script"); +""" + var exp = """ +
    +

    This is a header.

    +
      +
    1. This is the first list item.
    2. +
    3. This is the second list item.
    4. +
    +

    Here's some example code:

    +
    return shell_exec("echo $input | $markdown_script");
    +
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_nesting2 do + var test = """ +* A list item with a blockquote: + + > This is a blockquote + > inside a list item. +""" + var exp = """ +
      +
    • A list item with a blockquote:

      +
      +

      This is a blockquote +inside a list item.

      +
      +
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_nesting3 do + var test = """ +* A list item with a code block: + + +""" + var exp = """ +
      +
    • A list item with a code block:

      +
      <code goes here>
      +
      +
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_nesting4 do + var test = """ +* Tab + * Tab + * Tab +""" + var exp = """ +
      +
    • Tab
        +
      • Tab
          +
        • Tab
        • +
        +
      • +
      +
    • +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + # TODO + # fun test_process_nesting5 do + # var test = """ + # * this + # + # * sub + # + # that + # """ + # var exp = """ + #
      + #
    • this

      + #
        + #
      • sub
      • + #
      + #

      that

      + #
    • + #
    + # """ + # var res = test.md_to_html.write_to_string + # assert res == exp + # end + + fun test_process_emph1 do + var test = """ +*single asterisks* + +_single underscores_ + +**double asterisks** + +__double underscores__ +""" + var exp = """

    single asterisks

    +

    single underscores

    +

    double asterisks

    +

    double underscores

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_emph2 do + var test = "un*frigging*believable" + var exp = "

    unfriggingbelievable

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_xml1 do + var test = """ +This is a regular paragraph. + + + + + +
    Foo
    + +This is another regular paragraph. +""" + var exp = """ +

    This is a regular paragraph.

    + + + + +
    Foo
    +

    This is another regular paragraph.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_xml2 do + var test = """ +This is an image baz in a regular paragraph. +""" + var exp = """

    This is an image baz in a regular paragraph.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_xml3 do + var test = """ +
    +""" + var exp = """ +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_span_code1 do + var test = "Use the `printf()` function." + var exp = "

    Use the printf() function.

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_span_code2 do + var test = "``There is a literal backtick (`) here.``" + var exp = "

    There is a literal backtick (`) here.

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_span_code3 do + var test = """ +A single backtick in a code span: `` ` `` + +A backtick-delimited string in a code span: `` `foo` `` +""" + var exp = """ +

    A single backtick in a code span: `

    +

    A backtick-delimited string in a code span: `foo`

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_span_code4 do + var test = "Please don't use any `` tags." + var exp = "

    Please don't use any <blink> tags.

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_span_code5 do + var test = "`—` is the decimal-encoded equivalent of `—`." + var exp = "

    &#8212; is the decimal-encoded equivalent of &mdash;.

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_escape1 do + var test = "\\*this text is surrounded by literal asterisks\\*" + var exp = "

    *this text is surrounded by literal asterisks*

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_escape2 do + var test = "1986\\. What a great season." + var exp = "

    1986. What a great season.

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_escape3 do + var test = "Ben & Lux" + var exp = "

    Ben & Lux

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link1 do + var test = """ +This is [an example](http://example.com/ "Title") inline link. + +[This link](http://example.net/) has no title attribute. +""" + var exp = """

    This is an example inline link.

    +

    This link has no title attribute.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link2 do + var test = "See my [About](/about/) page for details." + var exp = "

    See my About page for details.

    \n" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link3 do + var test = """ +This is [an example][id] reference-style link. + +This is [an example] [id] reference-style link. + +Some lorem ipsum + +[id]: http://example.com/ "Optional Title Here" + +Some other lipsum +""" + var exp = """ +

    This is an example reference-style link.

    +

    This is an example reference-style link.

    +

    Some lorem ipsum

    +

    Some other lipsum

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link4 do + var test = """ +This is multiple examples: [foo][1], [bar][2], [baz][3]. + +[1]: http://example.com/ "Optional Title Here" +[2]: http://example.com/ 'Optional Title Here' +[3]: http://example.com/ (Optional Title Here) +""" + var exp = """ +

    This is multiple examples: foo, bar, baz.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link5 do + var test = """ +This is multiple examples: [foo][a], [bar][A], [a]. + +[a]: http://example.com/ "Optional Title Here" +""" + var exp = """

    This is multiple examples: foo, bar, a.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link6 do + var test = """ +I get 10 times more traffic from [Google][] than from [Yahoo][] or [MSN][]. + +[Google]: http://google.com/ "Google" +[Yahoo]: http://search.yahoo.com/ "Yahoo Search" +[MSN]: http://search.msn.com/ "MSN Search" +""" + var exp = """

    I get 10 times more traffic from Google than from Yahoo or MSN.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link7 do + var test = """ +Visit [Daring Fireball][] for more information. + +[Daring Fireball]: http://daringfireball.net/ +""" + var exp = """

    Visit Daring Fireball for more information.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_link8 do + var test = """ +This one has a [line +break]. + +This one has a [line +break] with a line-ending space. + +[line break]: /foo +""" + var exp = """ +

    This one has a line +break.

    +

    This one has a line +break with a line-ending space.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + # FIXME unignore test once escape strings fixed + # fun test_process_link9 do + # var test = """ + # Foo [bar][]. + # + # Foo [bar](/url/ "Title with \"quotes\" inside"). + # + # + # [bar]: /url/ "Title with \"quotes\" inside" + # """ + # var exp = """ + #

    Foo bar.

    + #

    Foo bar.

    + # """ + # var res = test.md_to_html.write_to_string + # assert res == exp + # end + + fun test_process_img1 do + var test = """ +![Alt text](/path/to/img.jpg) + +![Alt text](/path/to/img.jpg "Optional title") +""" + var exp = """

    Alt text

    +

    Alt text

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_process_img2 do + var test = """ +![Alt text][id] + +[id]: url/to/image "Optional title attribute" +""" + var exp = """

    Alt text

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_encoding do + var test = """ +AT&T has an ampersand in their name. + +AT&T is another way to write it. + +This & that. + +4 < 5. + +6 > 5. + +Here's a [link] [1] with an ampersand in the URL. + +Here's a link with an amersand in the link text: [AT&T] [2]. + +Here's an inline [link](/script?foo=1&bar=2). + +Here's an inline [link](). + + +[1]: http://example.com/?foo=1&bar=2 +[2]: http://att.com/ "AT&T" +""" + + var exp = """ +

    AT&T has an ampersand in their name.

    +

    AT&T is another way to write it.

    +

    This & that.

    +

    4 < 5.

    +

    6 > 5.

    +

    Here's a link with an ampersand in the URL.

    +

    Here's a link with an amersand in the link text: AT&T.

    +

    Here's an inline link.

    +

    Here's an inline link.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + + end + + fun test_daring_autolinks do + var test = """ +Link: . + +With an ampersand: + +* In a list? +* +* It should. + +> Blockquoted: + +Auto-links should not occur here: `` + + or here: +""" + + var exp = """ +

    Link: http://example.com/.

    +

    With an ampersand: http://example.com/?foo=1&bar=2

    + +
    +

    Blockquoted: http://example.com/

    +
    +

    Auto-links should not occur here: <http://example.com/>

    +
    or here: <http://example.com/>
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_escape do + var test = """ +These should all get escaped: + +Backslash: \\ + +Backtick: \` + +Asterisk: \* + +Underscore: \_ + +Left brace: \{ + +Right brace: \} + +Left bracket: \[ + +Right bracket: \] + +Left paren: \( + +Right paren: \) + +Greater-than: \> + +Hash: \# + +Period: \. + +Bang: \! + +Plus: \+ + +Minus: \- + + +These should not, because they occur within a code block: + + Backslash: \\ + + Backtick: \` + + Asterisk: \* + + Underscore: \_ + + Left brace: \{ + + Right brace: \} + + Left bracket: \[ + + Right bracket: \] + + Left paren: \( + + Right paren: \) + + Greater-than: \> + + Hash: \# + + Period: \. + + Bang: \! + + Plus: \+ + + Minus: \- + +Nor should these, which occur in code spans: + +Backslash: `\\` + +Backtick: `` \` `` + +Asterisk: `\*` + +Underscore: `\_` + +Left brace: `\{` + +Right brace: `\}` + +Left bracket: `\[` + +Right bracket: `\]` + +Left paren: `\(` + +Right paren: `\)` + +Greater-than: `\>` + +Hash: `\#` + +Period: `\.` + +Bang: `\!` + +Plus: `\+` + +Minus: `\-` + +These should get escaped, even though they're matching pairs for +other Markdown constructs: + +\\\*asterisks\\\* + +\\\_underscores\\\_ + +\\\`backticks\\\` + +This is a code span with a literal backslash-backtick sequence: `` \` `` + +This is a tag with unescaped backticks bar. + +This is a tag with backslashes bar. +""" + var exp = """ +

    These should all get escaped:

    +

    Backslash: \\

    +

    Backtick: \`

    +

    Asterisk: \*

    +

    Underscore: \_

    +

    Left brace: \{

    +

    Right brace: \}

    +

    Left bracket: \[

    +

    Right bracket: \]

    +

    Left paren: \(

    +

    Right paren: \)

    +

    Greater-than: \>

    +

    Hash: \#

    +

    Period: \.

    +

    Bang: \!

    +

    Plus: \+

    +

    Minus: \-

    +

    These should not, because they occur within a code block:

    +
    Backslash: \\
    +
    +Backtick: \`
    +
    +Asterisk: \*
    +
    +Underscore: \_
    +
    +Left brace: \{
    +
    +Right brace: \}
    +
    +Left bracket: \[
    +
    +Right bracket: \]
    +
    +Left paren: \(
    +
    +Right paren: \)
    +
    +Greater-than: \>
    +
    +Hash: \#
    +
    +Period: \.
    +
    +Bang: \!
    +
    +Plus: \+
    +
    +Minus: \-
    +
    +

    Nor should these, which occur in code spans:

    +

    Backslash: \\

    +

    Backtick: \`

    +

    Asterisk: \*

    +

    Underscore: \_

    +

    Left brace: \{

    +

    Right brace: \}

    +

    Left bracket: \[

    +

    Right bracket: \]

    +

    Left paren: \(

    +

    Right paren: \)

    +

    Greater-than: \>

    +

    Hash: \#

    +

    Period: \.

    +

    Bang: \!

    +

    Plus: \+

    +

    Minus: \-

    +

    These should get escaped, even though they're matching pairs for +other Markdown constructs:

    +

    *asterisks*

    +

    _underscores_

    +

    `backticks`

    +

    This is a code span with a literal backslash-backtick sequence: \`

    +

    This is a tag with unescaped backticks bar.

    +

    This is a tag with backslashes bar.

    +""" + + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_blockquotes do + var test = """ +> Example: +> +> sub status { +> print "working"; +> } +> +> Or: +> +> sub status { +> return "working"; +> } +""" + + var exp = """ +
    +

    Example:

    +
    sub status {
    +    print "working";
    +}
    +
    +

    Or:

    +
    sub status {
    +    return "working";
    +}
    +
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_code_blocks do + var test = """ + code block on the first line + +Regular text. + + code block indented by spaces + +Regular text. + + the lines in this block + all contain trailing spaces + +Regular Text. + + code block on the last line +""" + + var exp = """ +
    code block on the first line
    +
    +

    Regular text.

    +
    code block indented by spaces
    +
    +

    Regular text.

    +
    the lines in this block
    +all contain trailing spaces
    +
    +

    Regular Text.

    +
    code block on the last line
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_code_spans do + var test = """ +`` + +Fix for backticks within HTML tag: like this + +Here's how you put `` `backticks` `` in a code span. +""" + + var exp = """ +

    <test a=" content of attribute ">

    +

    Fix for backticks within HTML tag: like this

    +

    Here's how you put `backticks` in a code span.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_pars do + var test = """ +In Markdown 1.0.0 and earlier. Version +8. This line turns into a list item. +Because a hard-wrapped line in the +middle of a paragraph looked like a +list item. + +Here's one with a bullet. +* criminey. +""" + + var exp = """ +

    In Markdown 1.0.0 and earlier. Version +8. This line turns into a list item. +Because a hard-wrapped line in the +middle of a paragraph looked like a +list item.

    +

    Here's one with a bullet. +* criminey.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_rules do + var test = """ +Dashes: + +--- + + --- + + --- + + --- + + --- + +- - - + + - - - + + - - - + + - - - + + - - - + + +Asterisks: + +*** + + *** + + *** + + *** + + *** + +* * * + + * * * + + * * * + + * * * + + * * * + + +Underscores: + +___ + + ___ + + ___ + + ___ + + ___ + +_ _ _ + + _ _ _ + + _ _ _ + + _ _ _ + + _ _ _ +""" + + var exp = """ +

    Dashes:

    +
    +
    +
    +
    +
    ---
    +
    +
    +
    +
    +
    +
    - - -
    +
    +

    Asterisks:

    +
    +
    +
    +
    +
    ***
    +
    +
    +
    +
    +
    +
    * * *
    +
    +

    Underscores:

    +
    +
    +
    +
    +
    ___
    +
    +
    +
    +
    +
    +
    _ _ _
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_images do + var test = """ +![Alt text](/path/to/img.jpg) + +![Alt text](/path/to/img.jpg "Optional title") + +Inline within a paragraph: [alt text](/url/). + +![alt text](/url/ "title preceded by two spaces") + +![alt text](/url/ "title has spaces afterward" ) + +![alt text]() + +![alt text]( "with a title"). + +![Empty]() + +![this is a stupid URL](http://example.com/(parens).jpg) + + +![alt text][foo] + + [foo]: /url/ + +![alt text][bar] + + [bar]: /url/ "Title here" +""" + + var exp = """ +

    Alt text

    +

    Alt text

    +

    Inline within a paragraph: alt text.

    +

    alt text

    +

    alt text

    +

    alt text

    +

    alt text.

    +

    Empty

    +

    this is a stupid URL

    +

    alt text

    +

    alt text

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_inline_html1 do + var test = """ +Here's a simple block: + +
    + foo +
    + +This should be a code block, though: + +
    + foo +
    + +As should this: + +
    foo
    + +Now, nested: + +
    +
    +
    + foo +
    +
    +
    + +This should just be an HTML comment: + + + +Multiline: + + + +Code block: + + + +Just plain comment, with trailing spaces on the line: + + + +Code: + +
    + +Hr's: + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +""" + + var exp = """ +

    Here's a simple block:

    +
    + foo +
    +

    This should be a code block, though:

    +
    <div>
    +    foo
    +</div>
    +
    +

    As should this:

    +
    <div>foo</div>
    +
    +

    Now, nested:

    +
    +
    +
    + foo +
    +
    +
    +

    This should just be an HTML comment:

    + +

    Multiline:

    + +

    Code block:

    +
    <!-- Comment -->
    +
    +

    Just plain comment, with trailing spaces on the line:

    + +

    Code:

    +
    <hr />
    +
    +

    Hr's:

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_inline_html2 do + var test = """ +Simple block on one line: + +
    foo
    + +And nested without indentation: + +
    +
    +
    +foo +
    +
    +
    +
    bar
    +
    + +And with attributes: + +
    +
    +
    +
    + +This was broken in 1.0.2b7: + +
    +
    +foo +
    +
    +""" + + var exp = """ +

    Simple block on one line:

    +
    foo
    +

    And nested without indentation:

    +
    +
    +
    +foo +
    +
    +
    +
    bar
    +
    +

    And with attributes:

    +
    +
    +
    +
    +

    This was broken in 1.0.2b7:

    +
    +
    +foo +
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_inline_html3 do + var test = """ +Paragraph one. + + + + + +Paragraph two. + + + +The end. +""" + + var exp = """ +

    Paragraph one.

    + + +

    Paragraph two.

    + +

    The end.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_links1 do + var test = """ +Just a [URL](/url/). + +[URL and title](/url/ "title"). + +[URL and title](/url/ "title preceded by two spaces"). + +[URL and title](/url/ "title preceded by a tab"). + +[URL and title](/url/ "title has spaces afterward" ). + +[URL wrapped in angle brackets](). + +[URL w/ angle brackets + title]( "Here's the title"). + +[Empty](). + +[With parens in the URL](http://en.wikipedia.org/wiki/WIMP_(computing)) + +(With outer parens and [parens in url](/foo(bar))) + + +[With parens in the URL](/foo(bar) "and a title") + +(With outer parens and [parens in url](/foo(bar) "and a title")) +""" + + var exp = """ +

    Just a URL.

    +

    URL and title.

    +

    URL and title.

    +

    URL and title.

    +

    URL and title.

    +

    URL wrapped in angle brackets.

    +

    URL w/ angle brackets + title.

    +

    Empty.

    +

    With parens in the URL

    +

    (With outer parens and parens in url)

    +

    With parens in the URL

    +

    (With outer parens and parens in url)

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_links2 do + var test = """ +Foo [bar] [1]. + +Foo [bar][1]. + +Foo [bar] +[1]. + +[1]: /url/ "Title" + + +With [embedded [brackets]] [b]. + + +Indented [once][]. + +Indented [twice][]. + +Indented [thrice][]. + +Indented [four][] times. + + [once]: /url + + [twice]: /url + + [thrice]: /url + + [four]: /url + + +[b]: /url/ + +* * * + +[this] [this] should work + +So should [this][this]. + +And [this] []. + +And [this][]. + +And [this]. + +But not [that] []. + +Nor [that][]. + +Nor [that]. + +[Something in brackets like [this][] should work] + +[Same with [this].] + +In this case, [this](/somethingelse/) points to something else. + +Backslashing should suppress \\\[this] and [this\\\]. + +[this]: foo + + +* * * + +Here's one where the [link +breaks] across lines. + +Here's another where the [link +breaks] across lines, but with a line-ending space. + + +[link breaks]: /url/ +""" + + var exp = """ +

    Foo bar.

    +

    Foo bar.

    +

    Foo bar.

    +

    With embedded [brackets].

    +

    Indented once.

    +

    Indented twice.

    +

    Indented thrice.

    +

    Indented [four][] times.

    +
    [four]: /url
    +
    +
    +

    this should work

    +

    So should this.

    +

    And this.

    +

    And this.

    +

    And this.

    +

    But not [that] [].

    +

    Nor [that][].

    +

    Nor [that].

    +

    [Something in brackets like this should work]

    +

    [Same with this.]

    +

    In this case, this points to something else.

    +

    Backslashing should suppress [this] and [this].

    +
    +

    Here's one where the link +breaks across lines.

    +

    Here's another where the link +breaks across lines, but with a line-ending space.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_links3 do + var test = """ +This is the [simple case]. + +[simple case]: /simple + + + +This one has a [line +break]. + +This one has a [line +break] with a line-ending space. + +[line break]: /foo + + +[this] [that] and the [other] + +[this]: /this +[that]: /that +[other]: /other +""" + + var exp = """ +

    This is the simple case.

    +

    This one has a line +break.

    +

    This one has a line +break with a line-ending space.

    +

    this and the other

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_nested do + var test = """ +> foo +> +> > bar +> +> foo +""" + + var exp = """ +
    +

    foo

    +
    +

    bar

    +
    +

    foo

    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_list do + var test = """ +## Unordered + +Asterisks tight: + +* asterisk 1 +* asterisk 2 +* asterisk 3 + + +Asterisks loose: + +* asterisk 1 + +* asterisk 2 + +* asterisk 3 + +* * * + +Pluses tight: + ++ Plus 1 ++ Plus 2 ++ Plus 3 + + +Pluses loose: + ++ Plus 1 + ++ Plus 2 + ++ Plus 3 + +* * * + + +Minuses tight: + +- Minus 1 +- Minus 2 +- Minus 3 + + +Minuses loose: + +- Minus 1 + +- Minus 2 + +- Minus 3 + + +## Ordered + +Tight: + +1. First +2. Second +3. Third + +and: + +1. One +2. Two +3. Three + + +Loose using tabs: + +1. First + +2. Second + +3. Third + +and using spaces: + +1. One + +2. Two + +3. Three + +Multiple paragraphs: + +1. Item 1, graf one. + + Item 2. graf two. The quick brown fox jumped over the lazy dog's + back. + +2. Item 2. + +3. Item 3. + + + +## Nested + +* Tab + * Tab + * Tab + +Here's another: + +1. First +2. Second: + * Fee + * Fie + * Foe +3. Third + +Same thing but with paragraphs: + +1. First + +2. Second: + * Fee + * Fie + * Foe + +3. Third +""" + + var exp = """ +

    Unordered

    +

    Asterisks tight:

    +
      +
    • asterisk 1
    • +
    • asterisk 2
    • +
    • asterisk 3
    • +
    +

    Asterisks loose:

    +
      +
    • asterisk 1

      +
    • +
    • asterisk 2

      +
    • +
    • asterisk 3

      +
    • +
    +
    +

    Pluses tight:

    +
      +
    • Plus 1
    • +
    • Plus 2
    • +
    • Plus 3
    • +
    +

    Pluses loose:

    +
      +
    • Plus 1

      +
    • +
    • Plus 2

      +
    • +
    • Plus 3

      +
    • +
    +
    +

    Minuses tight:

    +
      +
    • Minus 1
    • +
    • Minus 2
    • +
    • Minus 3
    • +
    +

    Minuses loose:

    +
      +
    • Minus 1

      +
    • +
    • Minus 2

      +
    • +
    • Minus 3

      +
    • +
    +

    Ordered

    +

    Tight:

    +
      +
    1. First
    2. +
    3. Second
    4. +
    5. Third
    6. +
    +

    and:

    +
      +
    1. One
    2. +
    3. Two
    4. +
    5. Three
    6. +
    +

    Loose using tabs:

    +
      +
    1. First

      +
    2. +
    3. Second

      +
    4. +
    5. Third

      +
    6. +
    +

    and using spaces:

    +
      +
    1. One

      +
    2. +
    3. Two

      +
    4. +
    5. Three

      +
    6. +
    +

    Multiple paragraphs:

    +
      +
    1. Item 1, graf one.

      +

      Item 2. graf two. The quick brown fox jumped over the lazy dog's +back.

      +
    2. +
    3. Item 2.

      +
    4. +
    5. Item 3.

      +
    6. +
    +

    Nested

    +
      +
    • Tab
        +
      • Tab
          +
        • Tab
        • +
        +
      • +
      +
    • +
    +

    Here's another:

    +
      +
    1. First
    2. +
    3. Second:
        +
      • Fee
      • +
      • Fie
      • +
      • Foe
      • +
      +
    4. +
    5. Third
    6. +
    +

    Same thing but with paragraphs:

    +
      +
    1. First

      +
    2. +
    3. Second:

      +
        +
      • Fee
      • +
      • Fie
      • +
      • Foe
      • +
      +
    4. +
    5. Third

      +
    6. +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_strong_em do + var test = """ +***This is strong and em.*** + +So is ***this*** word. + +___This is strong and em.___ + +So is ___this___ word. +""" + + var exp = """ +

    This is strong and em.

    +

    So is this word.

    +

    This is strong and em.

    +

    So is this word.

    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_tabs do + var test = """ ++ this is a list item + indented with tabs + ++ this is a list item + indented with spaces + +Code: + + this code block is indented by one tab + +And: + + this code block is indented by two tabs + +And: + + + this is an example list item + indented with tabs + + + this is an example list item + indented with spaces +""" + + var exp = """ +
      +
    • this is a list item +indented with tabs

      +
    • +
    • this is a list item +indented with spaces

      +
    • +
    +

    Code:

    +
    this code block is indented by one tab
    +
    +

    And:

    +
        this code block is indented by two tabs
    +
    +

    And:

    +
    +   this is an example list item
    +    indented with tabs
    +
    ++   this is an example list item
    +    indented with spaces
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + fun test_daring_tidyness do + var test = """ +> A list within a blockquote: +> +> * asterisk 1 +> * asterisk 2 +> * asterisk 3 +""" + + var exp = """ +
    +

    A list within a blockquote:

    +
      +
    • asterisk 1
    • +
    • asterisk 2
    • +
    • asterisk 3
    • +
    +
    +""" + var res = test.md_to_html.write_to_string + assert res == exp + end + + +end + +class TestBlock + super TestSuite + + fun test_has_blocks do + var subject = new MDBlock + assert not subject.has_blocks + subject.first_block = new MDBlock + assert subject.has_blocks + end + + fun test_count_blocks do + var subject = new MDBlock + assert subject.count_blocks == 0 + subject.first_block = new MDBlock + assert subject.count_blocks == 1 + subject.first_block.next = new MDBlock + assert subject.count_blocks == 2 + end + + fun test_has_lines do + var subject = new MDBlock + assert not subject.has_lines + subject.first_line = new MDLine("") + assert subject.has_lines + end + + fun test_count_lines do + var subject = new MDBlock + assert subject.count_lines == 0 + subject.first_line = new MDLine("") + assert subject.count_lines == 1 + subject.first_line.next = new MDLine("") + assert subject.count_lines == 2 + end + + fun test_split do + var line1 = new MDLine("line1") + var line2 = new MDLine("line2") + var line3 = new MDLine("line3") + var subject = new MDBlock + subject.add_line line1 + subject.add_line line2 + subject.add_line line3 + var block = subject.split(line2) + assert subject.count_blocks == 1 + assert subject.count_lines == 1 + assert subject.first_line == line3 + assert block.count_blocks == 0 + assert block.count_lines == 2 + assert block.first_line == line1 + assert block.last_line == line2 + end + + fun test_add_line do + var subject = new MDBlock + assert subject.count_lines == 0 + subject.add_line new MDLine("") + assert subject.count_lines == 1 + subject.add_line new MDLine("") + assert subject.count_lines == 2 + end + + fun test_remove_line do + var line1 = new MDLine("line1") + var line2 = new MDLine("line2") + var line3 = new MDLine("line3") + var subject = new MDBlock + subject.add_line line1 + subject.add_line line2 + subject.add_line line3 + subject.remove_line(line2) + assert subject.count_lines == 2 + subject.remove_line(line1) + assert subject.count_lines == 1 + assert subject.first_line == line3 + assert subject.last_line == line3 + end + + fun test_transform_headline1 do + var subject = new MDBlock + var kind = new BlockHeadline(subject) + subject.add_line new MDLine(" # Title 1 ") + kind.transform_headline(subject) + assert kind.depth == 1 + assert subject.first_line.value == "Title 1" + end + + fun test_transform_headline2 do + var subject = new MDBlock + var kind = new BlockHeadline(subject) + subject.add_line new MDLine(" #####Title 5 ") + kind.transform_headline(subject) + assert kind.depth == 5 + assert subject.first_line.value == "Title 5" + end + + fun test_remove_quote_prefix do + var subject = new MDBlock + var kind = new BlockQuote(subject) + subject.add_line new MDLine(" > line 1") + subject.add_line new MDLine(" > line 2") + subject.add_line new MDLine(" > line 3") + kind.remove_block_quote_prefix(subject) + assert subject.first_line.value == "line 1" + assert subject.first_line.next.value == "line 2" + assert subject.first_line.next.next.value == "line 3" + end + + fun test_remove_leading_empty_lines_1 do + var block = new MDBlock + block.add_line new MDLine("") + block.add_line new MDLine("") + block.add_line new MDLine("") + block.add_line new MDLine("") + block.add_line new MDLine(" text") + block.add_line new MDLine("") + assert block.remove_leading_empty_lines + assert block.first_line.value == " text" + end + + fun test_remove_leading_empty_lines_2 do + var block = new MDBlock + block.add_line new MDLine(" text") + block.remove_leading_empty_lines + assert block.first_line.value == " text" + end + + fun test_remove_trailing_empty_lines_1 do + var block = new MDBlock + block.add_line new MDLine("") + block.add_line new MDLine("text") + block.add_line new MDLine("") + block.add_line new MDLine("") + block.add_line new MDLine("") + block.add_line new MDLine("") + assert block.remove_trailing_empty_lines + assert block.last_line.value == "text" + end + + fun test_remove_trailing_empty_lines_2 do + var block = new MDBlock + block.add_line new MDLine("text ") + assert not block.remove_trailing_empty_lines + assert block.last_line.value == "text " + end + + fun test_remove_surrounding_empty_lines do + var block = new MDBlock + block.add_line new MDLine("") + block.add_line new MDLine("text") + block.add_line new MDLine("") + block.add_line new MDLine("") + block.add_line new MDLine("") + block.add_line new MDLine("") + assert block.remove_surrounding_empty_lines + assert block.first_line.value == "text" + assert block.last_line.value == "text" + end +end + +class TestLine + super TestSuite + + var subject: MDLine + + init do end + + fun test_is_empty do + subject = new MDLine("") + assert subject.is_empty + subject = new MDLine(" ") + assert subject.is_empty + subject = new MDLine("test") + assert not subject.is_empty + subject = new MDLine(" test") + assert not subject.is_empty + end + + fun test_leading do + subject = new MDLine("") + assert subject.leading == 0 + subject = new MDLine(" ") + assert subject.leading == 4 + subject = new MDLine("test") + assert subject.leading == 0 + subject = new MDLine(" test") + assert subject.leading == 4 + end + + fun test_trailing do + subject = new MDLine("") + assert subject.trailing == 0 + subject = new MDLine(" ") + assert subject.trailing == 0 + subject = new MDLine("test ") + assert subject.trailing == 3 + subject = new MDLine(" test ") + assert subject.trailing == 1 + end + + fun test_line_type do + var v = new MarkdownProcessor + subject = new MDLine("") + assert subject.kind(v) isa LineEmpty + subject = new MDLine(" ") + assert subject.kind(v) isa LineEmpty + subject = new MDLine("text ") + assert subject.kind(v) isa LineOther + subject = new MDLine(" # Title") + assert subject.kind(v) isa LineHeadline + subject = new MDLine(" ### Title") + assert subject.kind(v) isa LineHeadline + subject = new MDLine(" code") + assert subject.kind(v) isa LineCode + subject = new MDLine(" ~~~") + assert subject.kind(v) isa LineFence + subject = new MDLine(" ```") + assert subject.kind(v) isa LineFence + subject = new MDLine(" Title ") + subject.next = new MDLine("== ") + assert subject.kind(v) isa LineHeadline1 + subject = new MDLine(" Title ") + subject.next = new MDLine("-- ") + assert subject.kind(v) isa LineHeadline2 + subject = new MDLine(" * * * ") + assert subject.kind(v) isa LineHR + subject = new MDLine(" *** ") + assert subject.kind(v) isa LineHR + subject = new MDLine("- -- ") + assert subject.kind(v) isa LineHR + subject = new MDLine("--------- ") + assert subject.kind(v) isa LineHR + subject = new MDLine(" >") + assert subject.kind(v) isa LineBlockquote + subject = new MDLine("

    ") + assert subject.kind(v) isa LineXML + subject = new MDLine("

    ") + assert subject.kind(v) isa LineOther + subject = new MDLine(" * foo") + assert subject.kind(v) isa LineUList + subject = new MDLine("- foo") + assert subject.kind(v) isa LineUList + subject = new MDLine("+ foo") + assert subject.kind(v) isa LineUList + subject = new MDLine("1. foo") + assert subject.kind(v) isa LineOList + subject = new MDLine(" 11111. foo") + assert subject.kind(v) isa LineOList + end + + fun test_count_chars do + subject = new MDLine("") + assert subject.count_chars('*') == 0 + subject = new MDLine("* ") + assert subject.count_chars('*') == 1 + subject = new MDLine(" * text") + assert subject.count_chars('*') == 0 + subject = new MDLine(" * * *") + assert subject.count_chars('*') == 3 + subject = new MDLine("text ** ") + assert subject.count_chars('*') == 0 + end + + fun test_count_chars_start do + subject = new MDLine("") + assert subject.count_chars_start('*') == 0 + subject = new MDLine("* ") + assert subject.count_chars_start('*') == 1 + subject = new MDLine(" * text") + assert subject.count_chars_start('*') == 1 + subject = new MDLine(" * * * text") + assert subject.count_chars_start('*') == 3 + subject = new MDLine("text ** ") + assert subject.count_chars_start('*') == 0 + end +end diff --git a/lib/mnit/mnit_app.nit b/lib/mnit/mnit_app.nit index 4c003a6..9987b8c 100644 --- a/lib/mnit/mnit_app.nit +++ b/lib/mnit/mnit_app.nit @@ -30,10 +30,10 @@ redef class App # Display to use by apps # Is null if the display is not available or not yet ready - var display: nullable D protected writable = null + var display: nullable D = null is protected writable # Received quit order - var quit: Bool writable = false + var quit: Bool = false is writable # App is visible? (vs minimized or in background) fun visible: Bool is abstract diff --git a/lib/mnit/mnit_fps.nit b/lib/mnit/mnit_fps.nit index cd30e83..2b2abf1 100644 --- a/lib/mnit/mnit_fps.nit +++ b/lib/mnit/mnit_fps.nit @@ -24,7 +24,7 @@ redef class App # Zero (or a negative value) means no limit. # # Applications can modify this value even during the main-loop. - var maximum_fps writable = 60 + var maximum_fps = 60 is writable # Current frame-rate # Updated each 5 seconds. diff --git a/lib/mnit/mnit_null.nit b/lib/mnit/mnit_null.nit index c966769..91bc359 100644 --- a/lib/mnit/mnit_null.nit +++ b/lib/mnit/mnit_null.nit @@ -77,7 +77,7 @@ class NullImage super Image var path: String redef fun to_s do return path - redef var scale redef writable = 1.0 + redef var scale = 1.0 is redef writable redef var width = 32 redef var height = 32 end diff --git a/lib/mnit/tileset.nit b/lib/mnit/tileset.nit index 7b473f3..b0eae74 100644 --- a/lib/mnit/tileset.nit +++ b/lib/mnit/tileset.nit @@ -80,11 +80,11 @@ class TileSetFont # Additional space to insert horizontally between characters # A negave value will display tile overlaped - var hspace: Int writable = 0 + var hspace: Int = 0 is writable # Additional space to insert vertically between characters # A negave value will display tile overlaped - var vspace: Int writable = 0 + var vspace: Int = 0 is writable # The glyph (tile) associated to the caracter `c` according to `chars` # Returns null if `c` is not in `chars` diff --git a/lib/neo4j/curl_json.nit b/lib/neo4j/curl_json.nit index 77d3650..f1a1da8 100644 --- a/lib/neo4j/curl_json.nit +++ b/lib/neo4j/curl_json.nit @@ -35,14 +35,14 @@ abstract class JsonCurlRequest end # OAuth token - var auth: nullable String writable + var auth: nullable String is writable # User agent (is used by github to contact devs in case of problems) # Eg. "Awesome-Octocat-App" - var user_agent: nullable String writable + var user_agent: nullable String is writable # HTTP headers to send - var headers: nullable HeaderMap writable = null + var headers: nullable HeaderMap = null is writable # init HTTP headers for Neo4j REST API @@ -125,7 +125,7 @@ end class JsonPOST super JsonCurlRequest - var data: nullable Jsonable writable = null + var data: nullable Jsonable = null is writable redef fun init_headers do super @@ -160,7 +160,7 @@ end class JsonPUT super JsonCurlRequest - var data: nullable Jsonable writable = null + var data: nullable Jsonable = null is writable redef fun init_headers do super diff --git a/lib/nitcc_runtime.nit b/lib/nitcc_runtime.nit index c71ea6b..74dcd43 100644 --- a/lib/nitcc_runtime.nit +++ b/lib/nitcc_runtime.nit @@ -116,7 +116,7 @@ abstract class Parser # Should the parser stop # Used by generated parsers - var stop writable = true + var stop = true is writable # Parse a full sequence of tokens and return a complete syntactic tree fun parse: Node @@ -318,7 +318,7 @@ abstract class Node end # The position of the node in the input stream - var position: nullable Position writable = null + var position: nullable Position = null is writable # Produce a graphiz file for the syntaxtic tree rooted at `self`. fun to_dot(filepath: String) @@ -423,7 +423,7 @@ abstract class NToken end # The text associated with the token - var text: String writable = "" + var text: String = "" is writable redef fun to_s do var res = super diff --git a/lib/nitcorn/file_server.nit b/lib/nitcorn/file_server.nit index 348383e..e3471a3 100644 --- a/lib/nitcorn/file_server.nit +++ b/lib/nitcorn/file_server.nit @@ -21,6 +21,7 @@ module file_server import reactor import sessions import media_types +import http_errors redef class String # Returns a `String` copy of `self` without any of the prefixed '/'s @@ -44,6 +45,12 @@ class FileServer # Root of `self` file system var root: String + # Error page template for a given `code` + fun error_page(code: Int): Streamable do return new ErrorTemplate(code) + + # Header of each directory page + var header: nullable Streamable = null is writable + redef fun answer(request, turi) do var response @@ -87,6 +94,12 @@ class FileServer links.add "{file}" end + var header = self.header + var header_code + if header != null then + header_code = header.write_to_string + else header_code = "" + response.body = """ @@ -96,6 +109,7 @@ class FileServer {{{title}}} + {{{header_code}}}

    {{{title}}}

      @@ -123,6 +137,12 @@ class FileServer else response = new HttpResponse(404) else response = new HttpResponse(403) + if response.status_code != 200 then + var tmpl = error_page(response.status_code) + if header != null and tmpl isa ErrorTemplate then tmpl.header = header + response.body = tmpl.to_s + end + return response end end diff --git a/lib/nitcorn/http_errors.nit b/lib/nitcorn/http_errors.nit new file mode 100644 index 0000000..dd50e0b --- /dev/null +++ b/lib/nitcorn/http_errors.nit @@ -0,0 +1,77 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# Offers `ErrorTemplate` to display error pages +module http_errors + +import template + +import http_response + +# A basic error page for the HTTP error `code` +class ErrorTemplate + super Template + + # HTTP error code + var code: Int is writable + + # Header on this page + var header: nullable Streamable = null is writable + + # Body to show with this page + var body: nullable Streamable = null is writable + + redef fun rendering + do + var code_message = http_status_codes[code] + var message + if code_message != null then + message = "Error {code}: {code_message}" + else message = "Error {code}" + + add """ + + + + + + """ + add message + add """ + + +""" + + var header = header + if header != null then add header + + add """ +
      +

      """ + add message + add "

      " + + var body = body + if body != null then add body + + add """ +
      + +""" + end + + redef fun to_s do return write_to_string +end diff --git a/lib/nitcorn/http_request.nit b/lib/nitcorn/http_request.nit index 1a49e0c..07b91ad 100644 --- a/lib/nitcorn/http_request.nit +++ b/lib/nitcorn/http_request.nit @@ -54,6 +54,9 @@ class HttpRequest # The arguments passed with the POST method var post_args = new HashMap[String, String] + + # The arguments passed with the POST or GET method (with a priority on POST) + var all_args = new HashMap[String, String] end # Utility class to parse a request string and build a `HttpRequest` @@ -96,7 +99,10 @@ class HttpRequestParser if http_request.url.has('?') then http_request.uri = first_line[1].substring(0, first_line[1].index_of('?')) http_request.query_string = first_line[1].substring_from(first_line[1].index_of('?')+1) + + var parse_url = parse_url http_request.get_args = parse_url + http_request.all_args.recover_with parse_url else http_request.uri = first_line[1] end @@ -113,6 +119,7 @@ class HttpRequestParser continue end http_request.post_args[parts[0]] = decoded + http_request.all_args[parts[0]] = decoded else print "POST Error: {line} format error on {line}" end diff --git a/lib/opts.nit b/lib/opts.nit index 19b8ff3..a1bffc1 100644 --- a/lib/opts.nit +++ b/lib/opts.nit @@ -29,19 +29,19 @@ abstract class Option var errors: Array[String] = new Array[String] # Is this option mandatory? - var mandatory: Bool writable = false + var mandatory: Bool = false is writable # Is this option hidden from `usage`? - var hidden: Bool writable = false + var hidden: Bool = false is writable # Has this option been read? - var read: Bool writable = false + var read: Bool = false is writable # Current value of this option - var value: VALUE writable + var value: VALUE is writable # Default value of this option - var default_value: VALUE writable + var default_value: VALUE is writable # Create a new option init(help: String, default: VALUE, names: nullable Array[String]) @@ -138,7 +138,7 @@ abstract class OptionParameter protected fun convert(str: String): VALUE is abstract # Is the parameter mandatory? - var parameter_mandatory: Bool writable = true + var parameter_mandatory: Bool = true is writable redef fun read_param(it) do diff --git a/lib/pnacl.nit b/lib/pnacl.nit index b997c3f..bc876c0 100644 --- a/lib/pnacl.nit +++ b/lib/pnacl.nit @@ -24,6 +24,9 @@ # Provides PNaCl support for Nit. module pnacl is platform +import standard +intrude import standard::stream + in "C Header" `{ #include "ppapi/c/pp_errors.h" #include "ppapi/c/ppp.h" diff --git a/lib/scene2d.nit b/lib/scene2d.nit index 18a5d13..ac5a571 100644 --- a/lib/scene2d.nit +++ b/lib/scene2d.nit @@ -23,7 +23,7 @@ abstract class LiveObject fun update do end # Controls whether `update' and `draw' are automatically called by `LiveGroup' - var exists writable = true + var exists = true is writable # Redefine this method to asks how to draw on a view fun draw(view: View) is abstract @@ -37,16 +37,16 @@ class Sprite super LiveObject # x coordinate of the center point - var x: Int writable = 0 + var x: Int = 0 is writable # y coordinate of the center point - var y: Int writable = 0 + var y: Int = 0 is writable # width of the sprite - var width: Int writable = 100 + var width: Int = 100 is writable # height of the sprite - var height: Int writable = 100 + var height: Int = 100 is writable fun left: Int do return x - width/2 fun right: Int do return x + width/2 @@ -54,10 +54,10 @@ class Sprite fun bottom: Int do return y + height/2 # x velocity (applied by `update') - var vx: Int writable = 0 + var vx: Int = 0 is writable # y velocity (applied by `update') - var vy: Int writable = 0 + var vy: Int = 0 is writable redef fun update do diff --git a/lib/socket/socket.nit b/lib/socket/socket.nit index d82e1e5..7dee2ef 100644 --- a/lib/socket/socket.nit +++ b/lib/socket/socket.nit @@ -18,6 +18,7 @@ module socket import socket_c +intrude import standard::stream # Portal for communication between two machines class Socket diff --git a/lib/standard/collection/abstract_collection.nit b/lib/standard/collection/abstract_collection.nit index a806c57..7320e83 100644 --- a/lib/standard/collection/abstract_collection.nit +++ b/lib/standard/collection/abstract_collection.nit @@ -188,7 +188,7 @@ class Container[E] init(e: E) do item = e # The stored item - var item: E writable + var item: E is writable end # This iterator is quite stupid since it is used for only one item. @@ -202,7 +202,7 @@ private class ContainerIterator[E] redef var is_ok: Bool = true - var _container: Container[E] + private var container: Container[E] end # Items can be removed from this collection @@ -929,7 +929,7 @@ private class CoupleMapIterator[K: Object, E] _iter.next end - var _iter: Iterator[Couple[K,E]] + private var iter: Iterator[Couple[K,E]] init(i: Iterator[Couple[K,E]]) do _iter = i end @@ -940,10 +940,10 @@ end class Couple[F, S] # The first element of the couple. - var first: F writable + var first: F is writable # The second element of the couple. - var second: S writable + var second: S is writable # Create a new instance with a first and a second object. init(f: F, s: S) diff --git a/lib/standard/collection/array.nit b/lib/standard/collection/array.nit index b23f533..61e8700 100644 --- a/lib/standard/collection/array.nit +++ b/lib/standard/collection/array.nit @@ -21,8 +21,7 @@ import abstract_collection abstract class AbstractArrayRead[E] super SequenceRead[E] - var _length: Int = 0 - redef fun length do return _length + redef var length = 0 redef fun is_empty do return _length == 0 @@ -347,14 +346,14 @@ class Array[E] end # The internal storage. - var _items: nullable NativeArray[E] = null + private var items: nullable NativeArray[E] = null # Do not use this method # FIXME: Remove it once modules can intrude non local modules fun intern_items: NativeArray[E] do return _items.as(not null) # The size of `_items`. - var _capacity: Int = 0 + private var capacity: Int = 0 redef fun ==(o) do @@ -391,9 +390,9 @@ private class ArrayIterator[E] _index = 0 end - var _index: Int = 0 - redef fun index do return _index - var _array: AbstractArrayRead[E] + redef var index = 0 + + private var array: AbstractArrayRead[E] end private class ArrayReverseIterator[E] @@ -417,7 +416,7 @@ class ArraySet[E: Object] super Set[E] # The stored elements. - var _array: Array[E] + private var array: Array[E] redef fun has(e) do return _array.has(e) @@ -475,7 +474,7 @@ private class ArraySetIterator[E: Object] init(iter: ArrayIterator[E]) do _iter = iter - var _iter: ArrayIterator[E] + private var iter: ArrayIterator[E] end @@ -531,7 +530,7 @@ class ArrayMap[K: Object, E] end # Internal storage. - var _items = new Array[Couple[K,E]] + private var items = new Array[Couple[K,E]] # fast remove the ith element of the array private fun remove_at_index(i: Int) @@ -541,7 +540,7 @@ class ArrayMap[K: Object, E] end # The last positive result given by a index(1) call - var _last_index: Int = 0 + private var last_index: Int = 0 # Where is the `key` in `_item`? # return -1 if not found diff --git a/lib/standard/collection/hash_collection.nit b/lib/standard/collection/hash_collection.nit index 2def742..7c90189 100644 --- a/lib/standard/collection/hash_collection.nit +++ b/lib/standard/collection/hash_collection.nit @@ -19,18 +19,18 @@ import array private abstract class HashCollection[K: Object, N: HashNode[Object]] super ArrayCapable[nullable N] - var _array: nullable NativeArray[nullable N] = null # Used to store items - var _capacity: Int = 0 # Size of _array - var _length: Int = 0 # Number of items in the map + private var array: nullable NativeArray[nullable N] = null # Used to store items + private var capacity: Int = 0 # Size of _array + private var the_length: Int = 0 # Number of items in the map - var _first_item: nullable N = null # First added item (used to visit items in nice order) - var _last_item: nullable N = null # Last added item (same) + private var first_item: nullable N = null # First added item (used to visit items in nice order) + private var last_item: nullable N = null # Last added item (same) # The last key accessed (used for cache) - var _last_accessed_key: nullable K = null + private var last_accessed_key: nullable K = null # The last node accessed (used for cache) - var _last_accessed_node: nullable N = null + private var last_accessed_node: nullable N = null # Return the index of the key k fun index_at(k: K): Int @@ -89,8 +89,8 @@ private abstract class HashCollection[K: Object, N: HashNode[Object]] _last_accessed_node = node # Enlarge if needed - var l = _length - _length = l + 1 + var l = _the_length + _the_length = l + 1 # Magic values determined empirically # We do not want to enlarge too much @@ -123,7 +123,7 @@ private abstract class HashCollection[K: Object, N: HashNode[Object]] end # Remove the item in the array - _length -= 1 + _the_length -= 1 prev = node._prev_in_bucklet next = node._next_in_bucklet if prev != null then @@ -146,7 +146,7 @@ private abstract class HashCollection[K: Object, N: HashNode[Object]] _array[i] = null i -= 1 end - _length = 0 + _the_length = 0 _first_item = null _last_item = null _last_accessed_key = null @@ -157,7 +157,7 @@ private abstract class HashCollection[K: Object, N: HashNode[Object]] do var old_cap = _capacity # get a new capacity - if cap < _length + 1 then cap = _length + 1 + if cap < _the_length + 1 then cap = _the_length + 1 if cap <= _capacity then return _capacity = cap _last_accessed_key = null @@ -191,12 +191,12 @@ private abstract class HashCollection[K: Object, N: HashNode[Object]] end private abstract class HashNode[K: Object] - var _key: K + private var key: K type N: HashNode[K] - var _next_item: nullable N = null - var _prev_item: nullable N = null - var _prev_in_bucklet: nullable N = null - var _next_in_bucklet: nullable N = null + private var next_item: nullable N = null + private var prev_item: nullable N = null + private var prev_in_bucklet: nullable N = null + private var next_in_bucklet: nullable N = null init(k: K) do _key = k @@ -221,9 +221,9 @@ class HashMap[K: Object, V] redef fun iterator: HashMapIterator[K, V] do return new HashMapIterator[K,V](self) - redef fun length do return _length + redef fun length do return _the_length - redef fun is_empty do return _length == 0 + redef fun is_empty do return _the_length == 0 redef fun []=(key, v) do @@ -242,7 +242,7 @@ class HashMap[K: Object, V] init do _capacity = 0 - _length = 0 + _the_length = 0 enlarge(0) end @@ -345,7 +345,7 @@ end private class HashMapNode[K: Object, V] super HashNode[K] redef type N: HashMapNode[K, V] - var _value: V + private var value: V init(k: K, v: V) do @@ -383,10 +383,10 @@ class HashMapIterator[K: Object, V] end # The map to iterate on - var _map: HashMap[K, V] + private var map: HashMap[K, V] # The current node - var _node: nullable HashMapNode[K, V] + private var node: nullable HashMapNode[K, V] init(map: HashMap[K, V]) do @@ -401,13 +401,13 @@ class HashSet[E: Object] super Set[E] super HashCollection[E, HashSetNode[E]] - redef fun length do return _length + redef fun length do return _the_length - redef fun is_empty do return _length == 0 + redef fun is_empty do return _the_length == 0 redef fun first do - assert _length > 0 + assert _the_length > 0 return _first_item._key end @@ -436,7 +436,7 @@ class HashSet[E: Object] init do _capacity = 0 - _length = 0 + _the_length = 0 enlarge(0) end @@ -476,10 +476,10 @@ private class HashSetIterator[E: Object] end # The set to iterate on - var _set: HashSet[E] + private var set: HashSet[E] # The position in the internal map storage - var _node: nullable HashSetNode[E] + private var node: nullable HashSetNode[E] init(set: HashSet[E]) do diff --git a/lib/standard/collection/list.nit b/lib/standard/collection/list.nit index c5dc5dd..08a8459 100644 --- a/lib/standard/collection/list.nit +++ b/lib/standard/collection/list.nit @@ -210,10 +210,10 @@ class List[E] init from(coll: Collection[E]) do append(coll) # The first node of the list - var _head: nullable ListNode[E] + private var head: nullable ListNode[E] # The last node of the list - var _tail: nullable ListNode[E] + private var tail: nullable ListNode[E] # Get the `i`th node. get `null` otherwise. private fun get_node(i: Int): nullable ListNode[E] @@ -295,15 +295,13 @@ class ListIterator[E] end # The current list - var _list: List[E] + private var list: List[E] # The current node of the list - var _node: nullable ListNode[E] + private var node: nullable ListNode[E] # The index of the current node - var _index: Int - - redef fun index do return _index + redef var index # Remove the current item fun delete diff --git a/lib/standard/collection/range.nit b/lib/standard/collection/range.nit index e72771e..31ffaa2 100644 --- a/lib/standard/collection/range.nit +++ b/lib/standard/collection/range.nit @@ -76,9 +76,8 @@ end private class IteratorRange[E: Discrete] # Iterator on ranges. super Iterator[E] - var _range: Range[E] - var _item: E - redef fun item do return _item + private var range: Range[E] + redef var item redef fun is_ok do return _item < _range.after diff --git a/lib/standard/error.nit b/lib/standard/error.nit new file mode 100644 index 0000000..f40444d --- /dev/null +++ b/lib/standard/error.nit @@ -0,0 +1,91 @@ +# 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. + +# Standard error-management infrastructure. +# +module error + +import string + +# Standard class for error messages +class Error + # A short human-readable error message. + # + # This message is short and informative and could be displayed on the console, a dialog-box + # or written in a log file. + # + # Message should be explicative, autonomous and do not depend on contextual information. + # + # Eg. instead of "Fatal error: cannot open file", + # something like "File error, cannot open /some/path/document.ext, file not found." is preferred, + # where the message is informative as it, and the severity of the error is not assumed: + # while fatal for the library, it could be something benign for the program. + var message: String + + # An original error that caused the creation of this error, if any. + # + # This is used to chain errors and track the implication of various sub-systems for a given error. + # + # When displaying an error the end user, causes can be recursively displayed. + var cause: nullable Error = null is writable + + redef fun to_s do return message +end + +# Helper class used as a return value of methods that may give errors instead of values. +# +# Functions that return useful values or errors could use it to simulate an easy-to use multiple-return. +# +# ~~~ +# fun division(a,b: Int): MaybeError[Int, Error] +# do +# if b == 0 then return new MaybeError[Int, Error](null, new Error("Arithmetic Error: try to divide {a} by 0")) +# return new MaybeError[Int, Error](a / b, null) +# end +# +# assert division(10, 2).is_error == false +# assert division(10, 0).is_error == true +# ~~~ +# +# Clients has to handle the error: +# +# ~~~ +# var res = division(10, 2) +# if res.is_error then +# print res.error +# exit 1 +# end +# print res.value +# assert res.value == 5 +# ~~~ +class MaybeError[V, E: Error] + # The value, if any + var maybe_value: nullable V + + # The error, if any + var maybe_error: nullable E + + # It there an error? + fun is_error: Bool do return maybe_error != null + + # The value + # REQUIRE: `not is_error` + fun value: V do return maybe_value.as(V) + + # The require + # REQUIRE: `is_error` + fun error: E do return maybe_error.as(E) + + redef fun to_s do + var e = maybe_error + if e != null then return e.to_s + return value.to_s + end +end diff --git a/lib/standard/file.nit b/lib/standard/file.nit index f7883f6..06001df 100644 --- a/lib/standard/file.nit +++ b/lib/standard/file.nit @@ -36,7 +36,7 @@ abstract class FStream var path: nullable String = null # The FILE *. - var _file: nullable NativeFile = null + private var file: nullable NativeFile = null fun file_stat: FileStat do return _file.file_stat @@ -103,7 +103,7 @@ class OFStream redef fun write(s) do - assert _writable + assert _is_writable if s isa FlatText then write_native(s.to_cstring, s.length) else @@ -111,21 +111,18 @@ class OFStream end end - redef fun is_writable do return _writable - redef fun close do var i = _file.io_close - _writable = false + _is_writable = false end - # Is the file open in write mode - var _writable: Bool + redef var is_writable = false # Write `len` bytes from `native`. private fun write_native(native: NativeString, len: Int) do - assert _writable + assert _is_writable var err = _file.io_write(native, len) if err != len then # Big problem @@ -141,7 +138,7 @@ class OFStream print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'" end self.path = path - _writable = true + _is_writable = true end private init do end @@ -168,7 +165,7 @@ class Stdout private init do _file = new NativeFile.native_stdout path = "/dev/stdout" - _writable = true + _is_writable = true end end @@ -177,7 +174,7 @@ class Stderr private init do _file = new NativeFile.native_stderr path = "/dev/stderr" - _writable = true + _is_writable = true end end @@ -529,13 +526,13 @@ end redef class Sys # Standard input - var stdin: PollableIStream protected writable = new Stdin + var stdin: PollableIStream = new Stdin is protected writable # Standard output - var stdout: OStream protected writable = new Stdout + var stdout: OStream = new Stdout is protected writable # Standard output for errors - var stderr: OStream protected writable = new Stderr + var stderr: OStream = new Stderr is protected writable end diff --git a/lib/standard/re.nit b/lib/standard/re.nit new file mode 100644 index 0000000..7baabd2 --- /dev/null +++ b/lib/standard/re.nit @@ -0,0 +1,408 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Alexis Laferrière +# +# 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. + +# Regular expression support for all services based on `Pattern` +# +# Implemented using libc regular expressions. +# +# The main entities are `Text::to_re` and `Regex`. +module re + +import string_search +import gc +import error + +in "C Header" `{ + #include + #include +`} + +# Main extern class to wrap libc regular expression support +# +# It is recommanded to use the higher level API offered by the class `Regex`, +# but it can still be used for advanced purpose or in optimized code. +# +# To use this class and other `private` entities of this module, use `intrude import standard::re` +private extern class NativeRegex `{ regex_t* `} + # Allocate a new `NativeRegex`, it must then be compiled using `regcomp` before calling `regexec` + new malloc `{ return malloc(sizeof(regex_t)); `} + + # Compile the regular expression `regex` into a form that is suitable for subsequent `regexec` searches + fun regcomp(regex: NativeString, cflags: Int): Int `{ + return regcomp(recv, regex, cflags); + `} + + # Match `string` against the precompiled pattern buffer of `self`, locating matches + # + # `nmatch` and `pmatch` are used to provide information regarding the location of any matches. + # `eflags` may be the bitwise-or of one or both of `flag_notbol` and `flag_noteol`. + fun regexec(string: NativeString, nmatch: Int, pmatch: NativeMatchArray, eflags: Int): Int `{ + return regexec(recv, string, nmatch, pmatch, eflags); + `} + + # Match `string` against the precompiled pattern buffer of `self`, do not locate matches + # + # `eflags` may be the bitwise-or of one or both of `flag_notbol` and `flag_noteol`. + fun regexec_match_only(string: NativeString, eflags: Int): Int `{ + return regexec(recv, string, 0, NULL, eflags); + `} + + # Free the memory allocated to the pattern buffer by the compiling process + # + # Does not free the memory holding `self`, use `free` for this purpose. + fun regfree `{ regfree(recv); `} + + # Turn the error codes that can be returned by both `regcomp` and `regexec` into error message strings + fun regerror(errcode: Int): NativeString `{ + size_t len = regerror(errcode, recv, NULL, 0); + char *message = malloc(len); + regerror(errcode, recv, message, len); + + return message; + `} + + # This field holds the number of parenthetical subexpressions in the regular expression that was compiled. + fun re_nsub: Int `{ return recv->re_nsub; `} +end + +# Flags for `NativeRegex::regcomp` + +private fun flag_extended: Int `{ return REG_EXTENDED; `} +private fun flag_icase: Int `{ return REG_ICASE; `} +private fun flag_nosub: Int `{ return REG_NOSUB; `} +private fun flag_newline: Int `{ return REG_NEWLINE; `} + +# Flags for `NativeRegex::regexec` + +private fun flag_notbol: Int `{ return REG_NOTBOL; `} +private fun flag_noteol: Int `{ return REG_NOTEOL; `} + +# Errors of `NativeRegex::regexec` + +private fun error_nomatch: Int `{ return REG_NOMATCH; `} +private fun error_espace: Int `{ return REG_ESPACE; `} + +redef universal Int + private fun is_nomatch: Bool `{ return recv == REG_NOMATCH; `} +end + +# An array of `regmatch_t` or a pointer to one +private extern class NativeMatchArray `{ regmatch_t* `} + # Allocate a new array of `length` `regmatch_t` + new malloc(length: Int) `{ return malloc(length * sizeof(regmatch_t)); `} + + # The offset in string of the beginning of a substring + fun rm_so: Int `{ return recv->rm_so; `} + + # The offset in string of the end of the substring + fun rm_eo: Int `{ return recv->rm_eo; `} + + # Get a pointer to the element at `index`, can also be used as a subarray + fun [](index: Int): NativeMatchArray `{ return recv + index; `} +end + +redef extern class NativeString + private fun substring_from(index: Int): NativeString `{ return recv + index; `} +end + +redef class Text + # Get a `Regex` instance from `self` + fun to_re: Regex do return new Regex(self.to_s) +end + +# A regular expression pattern +# +# Used as a `Pattern` on intances of `Text` to call `has`, `search_all`, `replace`, etc. +# +# Example: +# +# var re = "ab+a".to_re +# assert "aabbbbaaaaba".search_all(re).join(", ") == "abbbba, aba" +# assert "aabbbbaaaaba".has(re) +# assert "aabbbbaaaaba".replace(re, "+") == "a+aa+" +# assert "aabbbbaaaaba".split(re) == ["a", "aa", ""] +class Regex + super Finalizable + super Pattern + + # The `String` source of this regular expression + var string: String is writable + + # Treat the pattern as a POSIX extended regular expression (the default) + # + # If `false`, it is treated as a POSIX basic regular expression (BRE). + # + # The extended syntax supports `?`, `+` and `|`. Also, `\` causes the following + # character to be used as literal. + var extended = true is writable + + # Ignore case when matching letters + var ignore_case = false is writable + + # Optimize `self` for `is_in` and `String::has`, but do not support searches + # + # If `true`, `self` cannont be used with `String::search_all`, `String::replace` + # or `String::split`. + var optimize_is_in = false is writable + + # Treat a newline in string as dividing string into multiple lines + # + # So that `$` can match before the newline and `^` can match after. + # Also, don’t permit `.` to match a newline, and don’t permit `[^…]` to match a newline. + # + # Otherwise, newline acts like any other ordinary character. + var newline = false is writable + + # Do not regard the beginning of the specified string as the beginning of a line + # + # More generally, don’t make any assumptions about what text might precede it. + var not_bol = false is writable + + # Do not regard the end of the specified string as the end of a line + # + # More generally, don’t make any assumptions about what text might follow it. + var not_eol = false is writable + + # Cache of the last used compiled regular expression + private var native: nullable NativeRegex = null + + # Cache of a single `regmatch_t` to prevent many calls to `malloc` + private var native_match = new NativeMatchArray.malloc(0) is lazy + + # `cflags` of the last successful `compile` + private var cflags_cache = 0 + + # `string` of the last successful `compile` + private var string_cache: nullable String = null + + # Compile the regular expression, if needed + # + # Return `null` on success and an `Error` otherwise. + # + # This method is always called by `get_match` and `has_match`, but the user + # should call it to check for errors. + # + # assert "ab".to_re.compile == null + # assert "[ab".to_re.compile.message == "Unmatched [ or [^" + fun compile: nullable Error + do + var cflags = 0 + if extended then cflags = cflags.bin_or(flag_extended) + if ignore_case then cflags = cflags.bin_or(flag_icase) + if optimize_is_in then cflags = cflags.bin_or(flag_nosub) + if newline then cflags = cflags.bin_or(flag_newline) + + var native = self.native + var need_compilation = native == null or cflags != cflags_cache or string != string_cache + + if need_compilation then + + # Initial allocation + if native == null then + native = new NativeRegex.malloc + self.native = native + end + + var res = native.regcomp(string.to_cstring, cflags) + + # All is good + if res == 0 then + # Update the cache + self.native = native + + # We store these to know if we need to recompile or not + self.cflags_cache = cflags + self.string_cache = string + + return null + end + + var error_cstr = native.regerror(res) + + # We leave it to the lib to decide how to allocate the string that we keep + var error_str = error_cstr.to_s_with_copy + error_cstr.free + + return new Error(error_str) + end + + return null + end + + redef fun finalize + do + var native = self.native + if native != null then + native.regfree + native.free + self.native = null + self.native_match.free + end + end + + private fun gather_eflags: Int + do + var eflags = 0 + if not_bol then eflags = eflags.bin_or(flag_notbol) + if not_eol then eflags = eflags.bin_or(flag_noteol) + return eflags + end + + private fun get_error(errcode: Int): String + do + # Error, should be out of memory but we cover any possible error anyway + var error_cstr = native.regerror(errcode) + + # We leave it to the lib to decide how to allocate the string that we keep + var error_str = error_cstr.to_s_with_copy + error_cstr.free + + return error_str + end + + # assert "ab".to_re.is_in("abcd") + # assert "ab".to_re.is_in("cdab") + # assert not "ab".to_re.is_in("acdb") + # assert "ab".to_re.is_in("ab") + redef fun is_in(text) + do + var comp_res = compile + assert comp_res == null else "Regex compilation failed with: {comp_res.message}\n".output + + # Actually execute + var eflags = gather_eflags + var res = native.regexec_match_only(text.to_cstring, eflags) + + # Got a match? + if res == 0 then return true + + # Got no match, not an error? + if res.is_nomatch then return false + + # Error, should be out of memory but we cover any possible error anyway + var error_str = get_error(res) + "Regex search failed with: {error_str}\n".output + abort + end + + # require: not optimize_is_in + # + # assert "l".to_re.search_index_in("hello world", 0) == 2 + # assert "el+o".to_re.search_index_in("hello world", 0) == 1 + # assert "l+".to_re.search_index_in("hello world", 3) == 3 + # assert "z".to_re.search_index_in("hello world", 0) == -1 + redef fun search_index_in(text, from) + do + assert not optimize_is_in + + var comp_res = compile + assert comp_res == null else "Regex compilation failed with: {comp_res.message}\n".output + + # Actually execute + text = text.to_s + var cstr = text.substring_from(from).to_cstring + var eflags = gather_eflags + var match = self.native_match + + var res = native.regexec(cstr, 1, match, eflags) + + # Found one? + if res == 0 then return match.rm_so + from + + # No more match? + if res.is_nomatch then return -1 + + # Error, should be out of memory but we cover any possible error anyway + var error_str = get_error(res) + "Regex search failed with: {error_str}\n".output + abort + end + + # require: not optimize_is_in + # + # assert "l".to_re.search_in("hello world", 0).from == 2 + # assert "el+o".to_re.search_in("hello world", 0).from == 1 + # assert "l+".to_re.search_in("hello world", 3).from == 3 + # assert "z".to_re.search_in("hello world", 0) == null + redef fun search_in(text, from) + do + assert not optimize_is_in + + var comp_res = compile + assert comp_res == null else "Regex compilation failed with: {comp_res.message}\n".output + + # Actually execute + text = text.to_s + var cstr = text.substring_from(from).to_cstring + var eflags = gather_eflags + var match = self.native_match + var matches = new Array[Match] + + var res = native.regexec(cstr, 1, match, eflags) + + # Found one? + if res == 0 then return new Match(text, from + match.rm_so, match.rm_eo - match.rm_so) + + # No more match? + if res.is_nomatch then return null + + # Error, should be out of memory but we cover any possible error anyway + var error_str = get_error(res) + "Regex search failed with: {error_str}\n".output + abort + end + + # require: not optimize_is_in + # + # assert "ab".to_re.search_all_in("abbab").join(", ") == "ab, ab" + # assert "b+".to_re.search_all_in("abbabaabbbbbcab").join(", ") == "bb, b, bbbbb, b" + redef fun search_all_in(text) + do + assert not optimize_is_in + + var comp_res = compile + assert comp_res == null else "Regex compilation failed with: {comp_res.message}\n".output + + # Actually execute + text = text.to_s + var cstr = text.to_cstring + var eflags = gather_eflags + var eflags_or_notbol = eflags.bin_or(flag_notbol) + var match = self.native_match + var matches = new Array[Match] + + var res = native.regexec(cstr, 1, match, eflags) + var d = 0 + while res == 0 do + matches.add new Match(text, d + match.rm_so, match.rm_eo - match.rm_so) + if d == match.rm_eo then + d += 1 + else d = d + match.rm_eo + cstr = cstr.substring_from(match.rm_eo) + res = native.regexec(cstr, 1, match, eflags_or_notbol) + end + + # No more match? + if res.is_nomatch then return matches + + # Error, should be out of memory but we cover any possible error anyway + var error_str = get_error(res) + "Regex search failed with: {error_str}\n".output + abort + end + + redef fun to_s do return "/{string}/" +end diff --git a/lib/standard/standard.nit b/lib/standard/standard.nit index 5becb03..4550113 100644 --- a/lib/standard/standard.nit +++ b/lib/standard/standard.nit @@ -30,3 +30,5 @@ import gc import bitset import queue import numeric +import error +import re diff --git a/lib/standard/stream.nit b/lib/standard/stream.nit index d14e82f..5932daa 100644 --- a/lib/standard/stream.nit +++ b/lib/standard/stream.nit @@ -250,10 +250,10 @@ abstract class BufferedIStream redef fun eof do return _buffer_pos >= _buffer.length and end_reached # The buffer - var _buffer: nullable FlatBuffer = null + private var buffer: nullable FlatBuffer = null # The current position in the buffer - var _buffer_pos: Int = 0 + private var buffer_pos: Int = 0 # Fill the buffer protected fun fill_buffer is abstract diff --git a/lib/standard/string.nit b/lib/standard/string.nit index 821672e..38bd492 100644 --- a/lib/standard/string.nit +++ b/lib/standard/string.nit @@ -16,6 +16,7 @@ module string import math import collection +intrude import collection::array `{ #include @@ -156,13 +157,6 @@ abstract class Text return self.chars.iterator end - # Is 'c' contained in self ? - # - # DEPRECATED : Use self.chars.has instead - fun has(c: Char): Bool - do - return self.chars.has(c) - end # Gets an Array containing the chars of self # @@ -178,7 +172,7 @@ abstract class Text # As with substring, a `from` index < 0 will be replaced by 0 fun substring_from(from: Int): SELFTYPE do - if from > self.length then return empty + if from >= self.length then return empty if from < 0 then from = 0 return substring(from, length - from) end @@ -364,7 +358,7 @@ abstract class Text if iter.item.ascii > 32 then break iter.next end - if iter.index == length then return self.empty + if iter.index < 0 then return self.empty return self.substring(0, iter.index + 1) end @@ -1136,7 +1130,7 @@ private class FlatStringReverseIterator curr_pos = pos + tgt.index_from end - redef fun is_ok do return curr_pos >= 0 + redef fun is_ok do return curr_pos >= target.index_from redef fun item do return target_items[curr_pos] @@ -1874,8 +1868,9 @@ redef class Array[E] else for j in tmp.substrings do var s = j.as(FlatString) - s.items.copy_to(ns, tpl, s.index_from, off) - off += tpl + var slen = s.length + s.items.copy_to(ns, slen, s.index_from, off) + off += slen end end i += 1 @@ -1967,7 +1962,7 @@ interface StringCapable end redef class Sys - var _args_cache: nullable Sequence[String] + private var args_cache: nullable Sequence[String] # The arguments of the program as given by the OS fun program_args: Sequence[String] diff --git a/lib/standard/string_search.nit b/lib/standard/string_search.nit index ef18e08..5aeac25 100644 --- a/lib/standard/string_search.nit +++ b/lib/standard/string_search.nit @@ -84,6 +84,8 @@ interface Pattern res.add(new Match(s.to_s, i, s.length - i)) return res end + + protected fun is_in(s: Text): Bool do return search_index_in(s, 0) != -1 end # BM_Pattern are pre-compiled string motif for the Boyer-Moore algorithm. @@ -149,10 +151,10 @@ class BM_Pattern end # searched motif - var _motif: String + private var motif: String # length of the motif - var _length: Int + private var length: Int private fun bc(e: Char): Int do @@ -164,10 +166,10 @@ class BM_Pattern end # good shifts - var _gs: Array[Int] + private var gs: Array[Int] # bad characters - var _bc_table: Map[Char, Int] + private var bc_table: Map[Char, Int] private fun compute_bc do @@ -374,4 +376,11 @@ redef class Text do return self.split_with(p).join(string) end + + # Does `self` contains at least one instance of `pattern`? + # + # assert "hello".has('l') + # assert "hello".has("ll") + # assert not "hello".has("lll") + fun has(pattern: Pattern): Bool do return pattern.is_in(self) end diff --git a/lib/string_experimentations/utf8.nit b/lib/string_experimentations/utf8.nit index 7261820..14f8550 100644 --- a/lib/string_experimentations/utf8.nit +++ b/lib/string_experimentations/utf8.nit @@ -401,7 +401,7 @@ end redef class OFStream redef fun write(s) do - assert _writable + assert is_writable if s isa FlatText then if s isa FlatString then write_native(s.to_cstring, s.bytelen) diff --git a/lib/string_experimentations/utf8_noindex.nit b/lib/string_experimentations/utf8_noindex.nit index bf49334..16c2502 100644 --- a/lib/string_experimentations/utf8_noindex.nit +++ b/lib/string_experimentations/utf8_noindex.nit @@ -732,7 +732,7 @@ end redef class OFStream redef fun write(s) do - assert _writable + assert is_writable if s isa FlatText then write_native(s.to_cstring, s.bytelen) else for i in s.substrings do write_native(i.to_cstring, i.length) diff --git a/lib/symbol.nit b/lib/symbol.nit index d268763..1e9fb67 100644 --- a/lib/symbol.nit +++ b/lib/symbol.nit @@ -31,7 +31,7 @@ end # A symbol is a unique immutable string class Symbol - var _string: String + private var string: String redef fun to_s do return _string.to_s # Only used by String::to_symbol diff --git a/lib/test_suite.nit b/lib/test_suite.nit new file mode 100644 index 0000000..6261eea --- /dev/null +++ b/lib/test_suite.nit @@ -0,0 +1,51 @@ +# 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. + +# Common interface for `nitunit` test-suites. +module test_suite is + # Annotation used by test-suite modules. + new_annotation test_suite +end + +# A test-suite that can be executed by `nitunit`. +# +# All test-suite classes must implement `TestSuite`. +class TestSuite + # Internal empty init. + private init nitunit do end + + # Method called before each test-case. + # + # Redefine this method to factorize code that have to be + # executed before every test. + fun before_test do end + + # Method called after each test-case. + # + # Redefine this method to factorize code that have to be + # executed after every test. + fun after_test do end +end + +# Method called before each test-suite. +# +# Redefine this method to factorize code that have to be +# executed before every test suite. +fun before_module do end + +# Method called after each test-suite. +# +# Redefine this method to factorize code that have to be +# executed after every test suite. +fun after_module do end diff --git a/lib/trees/abstract_tree.nit b/lib/trees/abstract_tree.nit index 0b79490..d05663e 100644 --- a/lib/trees/abstract_tree.nit +++ b/lib/trees/abstract_tree.nit @@ -28,7 +28,7 @@ abstract class TreeMap[K: Comparable, E] protected type N: TreeNode[K, E] # The `root` node of the tree (null if tree is empty) - protected var root: nullable N protected writable = null + protected var root: nullable N = null is protected writable # Display the tree in a gaphical windows # Graphviz with a working -Txlib is expected @@ -51,7 +51,7 @@ class TreeNode[K: Comparable, E] var value: E # Direct parent of this node (null if the node is root) - var parent: nullable SELF writable = null + var parent: nullable SELF = null is writable redef fun to_s do return "\{{value or else ""}\}" diff --git a/lib/websocket.nit b/lib/websocket.nit index 3179014..201fa92 100644 --- a/lib/websocket.nit +++ b/lib/websocket.nit @@ -22,6 +22,8 @@ import socket import sha1 import base64 +intrude import standard::stream + # Websocket compatible server, works as an extra layer to the original Sockets class WebSocket super BufferedIStream diff --git a/lib/x11.nit b/lib/x11.nit index b4f51d5..df0b984 100644 --- a/lib/x11.nit +++ b/lib/x11.nit @@ -15,7 +15,7 @@ # limitations under the License. # Serices from the X11 library -module x11 is pkgconfig("x11") +module x11 is pkgconfig # Open the current display from the environment variables # diff --git a/misc/vim/syntax_checkers/nit/nitg.vim b/misc/vim/syntax_checkers/nit/nitg.vim index f12f3ed..586b151 100644 --- a/misc/vim/syntax_checkers/nit/nitg.vim +++ b/misc/vim/syntax_checkers/nit/nitg.vim @@ -23,12 +23,12 @@ let loaded_syntastic_nit_nitg_checker = 1 if exists('g:syntastic_nitg') let s:nitg = g:syntastic_nitg else - let s:nitg = "nitc" + let s:nitg = "nitpick" endif if !executable(s:nitg) if exists('g:syntastic_nitg') - echo "Syntastic for Nit error: Custom nitg cannot be found at: " . g:syntastic_nitg + echo "Syntastic for Nit error: Custom tool cannot be found at: " . g:syntastic_nitg endif finish endif diff --git a/share/nitdoc/css/nitdoc.css b/share/nitdoc/css/nitdoc.css index 3f200bc..0fd73b7 100644 --- a/share/nitdoc/css/nitdoc.css +++ b/share/nitdoc/css/nitdoc.css @@ -147,23 +147,23 @@ h3 { #content>.col { height: 100%; - overflow: hidden; -} - -#content>.col:hover { overflow-y: scroll; } -#content>.col::-webkit-scrollbar { - width: 7px; - height: 7px; +#content>.col::-webkit-scrollbar-thumb { + background: transparent; } -#content>.col::-webkit-scrollbar-thumb { +#content>.col:hover::-webkit-scrollbar-thumb { background: #CCC; -webkit-box-shadow: inset 1px 1px 0 rgba(0,0,0,0.10),inset 0 -1px 0 rgba(0,0,0,0.07); } +#content>.col::-webkit-scrollbar { + width: 7px; + height: 7px; +} + #content>.col::-webkit-scrollbar-thumb:hover { background: #999; } diff --git a/src/Makefile b/src/Makefile index 44f2a21..2f3d7a1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,7 +16,7 @@ NITCOPT= OLDNITCOPT= -OBJS=nitdoc nitmetrics nitg nit nitx nitunit nitlight nitls nitdbg_client nitserial +OBJS=nitg nitpick nit nitdoc nitls nitunit nitpretty nitmetrics nitx nitlight nitdbg_client nitserial SRCS=$(patsubst %,%.nit,$(OBJS)) BINS=$(patsubst %,../bin/%,$(OBJS)) diff --git a/src/annotation.nit b/src/annotation.nit index 8d9ca25..6641280 100644 --- a/src/annotation.nit +++ b/src/annotation.nit @@ -15,9 +15,8 @@ # Management and utilities on annotations module annotation -import parser import modelbuilder -import literal +private import literal import model::mmodule_data redef class Prod diff --git a/src/astbuilder.nit b/src/astbuilder.nit index 3b5aceb..8e17b4e 100644 --- a/src/astbuilder.nit +++ b/src/astbuilder.nit @@ -15,10 +15,10 @@ # Instantiation and transformation of semantic nodes in the AST of expressions and statements module astbuilder -intrude import typing -intrude import literal -intrude import parser -intrude import scope +intrude import semantize::typing +intrude import semantize::literal +intrude import semantize::parser +intrude import semantize::scope # General factory to build semantic nodes in the AST of expressions class ASTBuilder diff --git a/src/astprinter.nit b/src/astprinter.nit index 97ff1fd..e0aceee 100644 --- a/src/astprinter.nit +++ b/src/astprinter.nit @@ -17,10 +17,9 @@ # print AST in an human form module astprinter -import typing -import phase +import semantize intrude import parser -import literal +private import literal private class ASTPrinterVisitor super Visitor diff --git a/src/astvalidation.nit b/src/astvalidation.nit index 913ee21..b78632b 100644 --- a/src/astvalidation.nit +++ b/src/astvalidation.nit @@ -15,7 +15,6 @@ # Check the consitency of AST module astvalidation -private import typing intrude import parser import astbuilder @@ -52,7 +51,7 @@ redef class ANode end v.seen.add(self) - if _location == null then + if not isset _location then #debug "LOCATION: unlocated node {v.path.join(", ")}" _location = self.parent.location end diff --git a/src/common_ffi/c.nit b/src/common_ffi/c.nit index 4e4402c..92697f5 100644 --- a/src/common_ffi/c.nit +++ b/src/common_ffi/c.nit @@ -72,8 +72,8 @@ redef class Location end redef class MModule - var c_compiler_options writable = "" - var c_linker_options writable = "" + var c_compiler_options = "" is writable + var c_linker_options = "" is writable end class ForeignCType diff --git a/src/common_ffi/common_ffi.nit b/src/common_ffi/common_ffi.nit index 302c472..814c882 100644 --- a/src/common_ffi/common_ffi.nit +++ b/src/common_ffi/common_ffi.nit @@ -19,7 +19,6 @@ # to wrap foreign code in Nit methods. module common_ffi -import parser import modelbuilder import nitni @@ -136,7 +135,7 @@ redef class VerifyNitniCallbacksPhase do super - if not npropdef isa AExternPropdef then return + if not npropdef isa AMethPropdef then return var code_block = npropdef.n_extern_code_block if code_block == null then return diff --git a/src/common_ffi/cpp.nit b/src/common_ffi/cpp.nit index 8cdce81..06f8f25 100644 --- a/src/common_ffi/cpp.nit +++ b/src/common_ffi/cpp.nit @@ -27,7 +27,7 @@ end redef class MModule private var cpp_file: nullable CPPCompilationUnit = null - var cpp_compiler_options writable = "" + var cpp_compiler_options = "" is writable end class CPPLanguage diff --git a/src/common_ffi/java.nit b/src/common_ffi/java.nit index 571c319..d3a7b68 100644 --- a/src/common_ffi/java.nit +++ b/src/common_ffi/java.nit @@ -251,7 +251,7 @@ redef class MModule private fun impl_java_class_name: String do return "Nit_{name}" end -redef class AExternPropdef +redef class AMethPropdef redef fun verify_nitni_callbacks(toolcontext) do super diff --git a/src/common_ffi/pkgconfig.nit b/src/common_ffi/pkgconfig.nit index 5306b97..335d8d1 100644 --- a/src/common_ffi/pkgconfig.nit +++ b/src/common_ffi/pkgconfig.nit @@ -20,6 +20,7 @@ module pkgconfig import c private import annotation +private import literal redef class ToolContext var pkgconfig_phase: Phase = new PkgconfigPhase(self, [literal_phase]) @@ -42,27 +43,29 @@ class PkgconfigPhase return end - var args = nat.n_args - if args.is_empty then - modelbuilder.error(nat, "Syntax error: \"pkgconfig\" expects at least one argument.") - return - end + # retreive module + var nmodule = nmoduledecl.parent.as(AModule) + var mmodule = nmodule.mmodule.as(not null) + # target pkgs var pkgs = new Array[String] - for arg in args do - var pkg = arg.as_string - if pkg == null then - modelbuilder.error(nat, "Syntax error: \"pkgconfig\" expects its arguments to be the name of the package as String literals.") - return - end - pkgs.add(pkg) + var args = nat.n_args + if args.is_empty then + # use module name + pkgs.add(mmodule.name) + else + for arg in args do + var pkg = arg.as_string + if pkg == null then + modelbuilder.error(nat, "Syntax error: \"pkgconfig\" expects its arguments to be the name of the package as String literals.") + return + end + + pkgs.add(pkg) + end end - # retreive module - var nmodule = nmoduledecl.parent.as(AModule) - var mmodule = nmodule.mmodule.as(not null) - # check availability of pkg-config var proc_which = new IProcess("which", "pkg-config") proc_which.wait diff --git a/src/abstract_compiler.nit b/src/compiler/abstract_compiler.nit similarity index 96% rename from src/abstract_compiler.nit rename to src/compiler/abstract_compiler.nit index 9e67eae..ef02aec 100644 --- a/src/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -18,10 +18,10 @@ module abstract_compiler import literal -import typing -import auto_super_init +import semantize import platform import c_tools +private import annotation # Add compiling options redef class ToolContext @@ -395,7 +395,7 @@ class MakefileToolchain if f.compiles_to_o_file then ofiles.add(o) if f.add_to_jar then java_files.add(f) end - + if not java_files.is_empty then var jar_file = "{outpath}.jar" @@ -445,13 +445,13 @@ abstract class AbstractCompiler # The main module of the program currently compiled # Is assigned during the separate compilation - var mainmodule: MModule writable + var mainmodule: MModule is writable # The real main module of the program var realmainmodule: MModule # The modeulbuilder used to know the model and the AST - var modelbuilder: ModelBuilder protected writable + var modelbuilder: ModelBuilder is protected writable # Is hardening asked? (see --hardening) fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value @@ -480,7 +480,7 @@ abstract class AbstractCompiler fun new_visitor: VISITOR is abstract # Where global declaration are stored (the main .h) - var header: CodeWriter writable + var header: CodeWriter is writable # Provide a declaration that can be requested (before or latter) by a visitor fun provide_declaration(key: String, s: String) @@ -643,10 +643,12 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); end # Generate the main C function. + # # This function: - # * allocate the Sys object if it exists - # * call init if is exists - # * call main if it exists + # + # * allocate the Sys object if it exists + # * call init if is exists + # * call main if it exists fun compile_main_function do var v = self.new_visitor @@ -930,12 +932,13 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { end # Display stats about compilation process + # # Metrics used: - # * type tests against resolved types (`x isa Collection[Animal]`) - # * type tests against unresolved types (`x isa Collection[E]`) - # * type tests skipped - # * type tests total - # * + # + # * type tests against resolved types (`x isa Collection[Animal]`) + # * type tests against unresolved types (`x isa Collection[E]`) + # * type tests skipped + # * type tests total fun display_stats do if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then @@ -1015,10 +1018,10 @@ abstract class AbstractCompilerVisitor var compiler: COMPILER # The current visited AST node - var current_node: nullable ANode writable = null + var current_node: nullable ANode = null is writable # The current `Frame` - var frame: nullable Frame writable + var frame: nullable Frame is writable # Alias for self.compiler.mainmodule.object_type fun object_type: MClassType do return self.compiler.mainmodule.object_type @@ -1261,7 +1264,7 @@ abstract class AbstractCompilerVisitor private var escapemark_names = new HashMap[EscapeMark, String] # Return a "const char*" variable associated to the classname of the dynamic type of an object - # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program + # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program fun class_name_string(value: RuntimeVariable): String is abstract # Variables handling @@ -1332,7 +1335,7 @@ abstract class AbstractCompilerVisitor var mtype = recv.mtype var finalizable_type = compiler.mainmodule.finalizable_type if finalizable_type != null and not mtype.need_anchor and - mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then + mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then add "gc_register_finalizer({recv});" end end @@ -1541,7 +1544,7 @@ abstract class AbstractRuntimeFunction # Non cached version of `c_name` protected fun build_c_name: String is abstract - protected var c_name_cache: nullable String writable = null + protected var c_name_cache: nullable String = null is writable # Implements a call of the runtime_function # May inline the body or generate a C function call @@ -1564,11 +1567,11 @@ class RuntimeVariable var mtype: MType # The current casted type of the variable (as known in Nit) - var mcasttype: MType writable + var mcasttype: MType is writable # If the variable exaclty a mcasttype? # false (usual value) means that the variable is a mcasttype or a subtype. - var is_exact: Bool writable = false + var is_exact: Bool = false is writable init(name: String, mtype: MType, mcasttype: MType) do @@ -1618,10 +1621,10 @@ class Frame var arguments: Array[RuntimeVariable] # The runtime_variable associated to the return (in a function) - var returnvar: nullable RuntimeVariable writable = null + var returnvar: nullable RuntimeVariable = null is writable # The label at the end of the property - var returnlabel: nullable String writable = null + var returnlabel: nullable String = null is writable end redef class MType @@ -1636,7 +1639,7 @@ redef class MType # Return the name of the C structure associated to a Nit live type fun c_name: String is abstract - protected var c_name_cache: nullable String protected writable + protected var c_name_cache: nullable String is protected writable end redef class MClassType @@ -1889,6 +1892,18 @@ redef class AMethPropdef v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) end + # Try special compilation + if mpropdef.is_intern then + if compile_intern_to_c(v, mpropdef, arguments) then return + else if mpropdef.is_extern then + if mpropdef.mproperty.is_init then + if compile_externinit_to_c(v, mpropdef, arguments) then return + else + if compile_externmeth_to_c(v, mpropdef, arguments) then return + end + end + + # Compile block if any var n_block = n_block if n_block != null then for i in [0..mpropdef.msignature.arity[ do @@ -1896,17 +1911,13 @@ redef class AMethPropdef v.assign(v.variable(variable), arguments[i+1]) end v.stmt(n_block) - else if mpropdef.is_intern then - compile_intern_to_c(v, mpropdef, arguments) - else if mpropdef.is_extern then - if mpropdef.mproperty.is_init then - compile_externinit_to_c(v, mpropdef, arguments) - else - compile_externmeth_to_c(v, mpropdef, arguments) - end - else - abort + return end + + # We have a problem + var cn = v.class_name_string(arguments.first) + v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});") + v.add_raw_abort end redef fun can_inline @@ -1919,7 +1930,7 @@ redef class AMethPropdef return false end - fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var pname = mpropdef.mproperty.name var cname = mpropdef.mclassdef.mclass.name @@ -1936,239 +1947,242 @@ redef class AMethPropdef if cname == "Int" then if pname == "output" then v.add("printf(\"%ld\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(arguments.first) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "%" then v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) - return + return true else if pname == "lshift" then v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) - return + return true else if pname == "rshift" then v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_f" then v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Char" then if pname == "output" then v.add("printf(\"%c\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "successor" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "predecessor" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Bool" then if pname == "output" then v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true end else if cname == "Float" then if pname == "output" then v.add("printf(\"%f\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(double){arguments.first}", ret.as(not null))) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "succ" then v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) - return + return true else if pname == "prec" then v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "NativeString" then if pname == "[]" then v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) - return + return true else if pname == "[]=" then v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};") - return + return true else if pname == "copy_to" then v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});") - return + return true else if pname == "atoi" then v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null))) - return + return true else if pname == "init" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true end else if cname == "NativeArray" then v.native_array_def(pname, ret, arguments) - return + return true end if pname == "exit" then v.add("exit({arguments[1]});") - return + return true else if pname == "sys" then v.ret(v.new_expr("glob_sys", ret.as(not null))) - return + return true else if pname == "calloc_string" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true else if pname == "calloc_array" then v.calloc_array(ret.as(not null), arguments) - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "is_same_type" then v.ret(v.is_same_type_test(arguments[0], arguments[1])) - return + return true else if pname == "is_same_instance" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "output_class_name" then var nat = v.class_name_string(arguments.first) v.add("printf(\"%s\\n\", {nat});") - return + return true else if pname == "native_class_name" then var nat = v.class_name_string(arguments.first) v.ret(v.new_expr("(char*){nat}", ret.as(not null))) - return + return true else if pname == "force_garbage_collection" then v.add("nit_gcollect();") - return + return true else if pname == "native_argc" then v.ret(v.new_expr("glob_argc", ret.as(not null))) - return + return true else if pname == "native_argv" then v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null))) - return + return true end - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");") - debug("Not implemented {mpropdef}") + return false end - fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern method + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + var nextern = self.n_extern + if nextern == null then return false + externname = nextern.text.substring(1, nextern.text.length-2) end - externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename v.add_extern(file) @@ -2189,18 +2203,23 @@ redef class AMethPropdef res = v.box_extern(res, ret.as(not null)) v.ret(res) end + return true end - fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern factory + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + var nextern = self.n_extern + if nextern == null then return false + externname = nextern.text.substring(1, nextern.text.length-2) end - externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename v.add_extern(file) @@ -2215,6 +2234,7 @@ redef class AMethPropdef v.add("{res} = {externname}({arguments.join(", ")});") res = v.box_extern(res, ret) v.ret(res) + return true end end @@ -3053,4 +3073,3 @@ for mmodule in mmodules do end toolcontext.run_global_phases(ms) end - diff --git a/src/android_annotations.nit b/src/compiler/android_annotations.nit similarity index 98% rename from src/android_annotations.nit rename to src/compiler/android_annotations.nit index ec0d2dc..a6fc4e2 100644 --- a/src/android_annotations.nit +++ b/src/compiler/android_annotations.nit @@ -18,11 +18,10 @@ # by calling `ModelBuilder::android_project_for`. module android_annotations -import parser_util -import modelbuilder -import modelize_property +private import parser_util +import modelize import literal -import typing +import semantize private import annotation # Metadata associated to an Android project @@ -137,7 +136,7 @@ redef class AAnnotation else for arg in args do var format_error = "Annotation error: \"{name}\" expects its arguments to be of type Int or a call to `git_revision`" - + var value value = arg.as_int if value != null then diff --git a/src/android_platform.nit b/src/compiler/android_platform.nit similarity index 99% rename from src/android_platform.nit rename to src/compiler/android_platform.nit index 8532cf8..c06a374 100644 --- a/src/android_platform.nit +++ b/src/compiler/android_platform.nit @@ -1,3 +1,4 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2014 Alexis Laferrière # diff --git a/src/coloring.nit b/src/compiler/coloring.nit similarity index 99% rename from src/coloring.nit rename to src/compiler/coloring.nit index aa65f32..ed9b466 100644 --- a/src/coloring.nit +++ b/src/compiler/coloring.nit @@ -386,5 +386,3 @@ class POSetBucketsColorer[H: Object, E: Object] return true end end - - diff --git a/src/compiler/compiler.nit b/src/compiler/compiler.nit new file mode 100644 index 0000000..902a656 --- /dev/null +++ b/src/compiler/compiler.nit @@ -0,0 +1,24 @@ +# 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. + +# Compilation to C +module compiler + +import separate_erasure_compiler +import global_compiler +import compiler_ffi + +import android_platform +import pnacl_platform +import emscripten_platform diff --git a/src/compiler_ffi.nit b/src/compiler/compiler_ffi.nit similarity index 98% rename from src/compiler_ffi.nit rename to src/compiler/compiler_ffi.nit index 4c55f65..331955e 100644 --- a/src/compiler_ffi.nit +++ b/src/compiler/compiler_ffi.nit @@ -110,8 +110,6 @@ redef class AMethPropdef amodule.ensure_compile_ffi_wrapper compile_ffi_method(mmodule) - assert self isa AExternPropdef - # nitni - Compile missing callbacks mmodule.ensure_compile_nitni_base(v) var ccu = mmodule.nitni_ccu.as(not null) @@ -144,15 +142,10 @@ redef class AMethPropdef redef fun compile_externmeth_to_c(v, mpropdef, arguments) do - var mmodule = mpropdef.mclassdef.mmodule - # if using the old native interface fallback on previous implementation - var nextern = self.n_extern - if nextern != null then - super - return - end + if n_extern_code_block == null then return super + var mmodule = mpropdef.mclassdef.mmodule mmodule.uses_ffi = true var mclass_type = mpropdef.mclassdef.bound_mtype @@ -205,19 +198,15 @@ redef class AMethPropdef end compile_ffi_support_to_c(v) + return true end redef fun compile_externinit_to_c(v, mpropdef, arguments) do - var mmodule = mpropdef.mclassdef.mmodule - # if using the old native interface fallback on previous implementation - var nextern = self.n_extern - if nextern != null then - super - return - end + if n_extern_code_block == null then return super + var mmodule = mpropdef.mclassdef.mmodule mmodule.uses_ffi = true var mclass_type = mpropdef.mclassdef.bound_mtype @@ -259,6 +248,7 @@ redef class AMethPropdef v.ret(recv_var) compile_ffi_support_to_c(v) + return true end end @@ -346,7 +336,7 @@ redef class MNullableType var base_cname = "null_{mtype.mangled_cname}" var full_cname = "NIT_NULL___{base_cname}" - # In nitni files, declare internal function as extern + # In nitni files, declare internal function as extern var full_friendly_csignature = "{cname_blind} {full_cname}()" ccu.header_decl.add("extern {full_friendly_csignature};\n") @@ -378,7 +368,7 @@ redef class MExplicitCall var mproperty = mproperty assert mproperty isa MMethod - # In nitni files, declare internal function as extern + # In nitni files, declare internal function as extern var full_friendly_csignature = mproperty.build_csignature(recv_mtype, v.compiler.mainmodule, null, long_signature, internal_call_context) ccu.header_decl.add("extern {full_friendly_csignature};\n") @@ -496,7 +486,7 @@ redef class MExplicitCast ## check type # - # In nitni files, declare internal function as extern + # In nitni files, declare internal function as extern var full_friendly_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({from.cname_blind})" ccu.header_decl.add("extern {full_friendly_csignature};\n") diff --git a/src/emscripten_platform.nit b/src/compiler/emscripten_platform.nit similarity index 100% rename from src/emscripten_platform.nit rename to src/compiler/emscripten_platform.nit diff --git a/src/global_compiler.nit b/src/compiler/global_compiler.nit similarity index 100% rename from src/global_compiler.nit rename to src/compiler/global_compiler.nit diff --git a/src/pnacl_platform.nit b/src/compiler/pnacl_platform.nit similarity index 100% rename from src/pnacl_platform.nit rename to src/compiler/pnacl_platform.nit diff --git a/src/separate_compiler.nit b/src/compiler/separate_compiler.nit similarity index 100% rename from src/separate_compiler.nit rename to src/compiler/separate_compiler.nit diff --git a/src/separate_erasure_compiler.nit b/src/compiler/separate_erasure_compiler.nit similarity index 100% rename from src/separate_erasure_compiler.nit rename to src/compiler/separate_erasure_compiler.nit diff --git a/src/doc/doc_model.nit b/src/doc/doc_model.nit index 9080dbf..01b7bc7 100644 --- a/src/doc/doc_model.nit +++ b/src/doc/doc_model.nit @@ -54,7 +54,7 @@ redef class MEntity # URL of this entity Nitdoc page fun nitdoc_url: String is abstract - # A template link to the mentity `nitdoc_anchor` + # A template link to the mentity `nitdoc_id` fun tpl_anchor: TplLink do var tpl = new TplLink("#{nitdoc_id}", nitdoc_name) if mdoc != null then @@ -186,7 +186,7 @@ redef class MGroup return "{mproject.nitdoc_id}__{nitdoc_name}" end - redef fun nitdoc_url do return "group_{name}.html" + redef fun nitdoc_url do return "group_{nitdoc_id}.html" redef fun tpl_namespace do var tpl = new Template @@ -229,16 +229,7 @@ redef class MModule return nitdoc_name end - redef fun nitdoc_url do - var res = new FlatBuffer - res.append("module_") - var mowner = public_owner - if mowner != null then - res.append("{public_owner.name}_") - end - res.append("{self.name}.html") - return res.to_s - end + redef fun nitdoc_url do return "module_{nitdoc_id}.html" redef fun tpl_declaration do var tpl = new Template @@ -272,8 +263,7 @@ end redef class MClass redef fun nitdoc_name do return name.html_escape redef fun nitdoc_id do return "{intro_mmodule.mgroup.mproject}__{name.to_cmangle}" - redef fun nitdoc_url do return "class_{public_owner}_{name}.html" - + redef fun nitdoc_url do return "class_{nitdoc_id}.html" redef fun mdoc do return intro.mdoc redef fun tpl_declaration do return intro.tpl_declaration @@ -439,7 +429,7 @@ end redef class MProperty redef fun nitdoc_name do return name.html_escape redef fun nitdoc_id do return "{intro_mclassdef.mclass.nitdoc_id}__{name.to_cmangle}" - redef fun nitdoc_url do return "proprety_{nitdoc_id}.html" + redef fun nitdoc_url do return "property_{nitdoc_id}.html" redef fun mdoc do return intro.mdoc diff --git a/src/doc/doc_pages.nit b/src/doc/doc_pages.nit index a49b76d..9c38d44 100644 --- a/src/doc/doc_pages.nit +++ b/src/doc/doc_pages.nit @@ -135,20 +135,20 @@ class Nitdoc end private fun overview do - var overviewpage = new NitdocOverview(ctx, model, mainmodule) - overviewpage.render.write_to_file("{ctx.output_dir.to_s}/index.html") + var page = new NitdocOverview(ctx, model, mainmodule) + page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end private fun search do - var searchpage = new NitdocSearch(ctx, model, mainmodule) - searchpage.render.write_to_file("{ctx.output_dir.to_s}/search.html") + var page = new NitdocSearch(ctx, model, mainmodule) + page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end private fun groups do for mproject in model.mprojects do for mgroup in mproject.mgroups.to_a do var page = new NitdocGroup(ctx, model, mainmodule, mgroup) - page.render.write_to_file("{ctx.output_dir.to_s}/{mgroup.nitdoc_url}") + page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end end end @@ -156,22 +156,22 @@ class Nitdoc private fun modules do for mmodule in model.mmodules do if mmodule.is_fictive then continue - var modulepage = new NitdocModule(ctx, model, mainmodule, mmodule) - modulepage.render.write_to_file("{ctx.output_dir.to_s}/{mmodule.nitdoc_url}") + var page = new NitdocModule(ctx, model, mainmodule, mmodule) + page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end end private fun classes do for mclass in model.mclasses do - var classpage = new NitdocClass(ctx, model, mainmodule, mclass) - classpage.render.write_to_file("{ctx.output_dir.to_s}/{mclass.nitdoc_url}") + var page = new NitdocClass(ctx, model, mainmodule, mclass) + page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end end private fun properties do for mproperty in model.mproperties do var page = new NitdocProperty(ctx, model, mainmodule, mproperty) - page.render.write_to_file("{ctx.output_dir.to_s}/{mproperty.nitdoc_url}") + page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end end @@ -265,6 +265,7 @@ abstract class NitdocPage # build page var tpl = tpl_page tpl.title = tpl_title + tpl.url = page_url tpl.shareurl = shareurl tpl.topmenu = tpl_topmenu tpl_content @@ -281,6 +282,9 @@ abstract class NitdocPage return tpl end + # URL to this page. + fun page_url: String is abstract + # Build page template fun tpl_page: TplPage is abstract @@ -297,7 +301,7 @@ abstract class NitdocPage # Build top menu template fun tpl_topmenu: TplTopMenu do - var topmenu = new TplTopMenu + var topmenu = new TplTopMenu(page_url) var brand = ctx.opt_custom_brand.value if brand != null then var tpl = new Template @@ -306,6 +310,8 @@ abstract class NitdocPage tpl.add "" topmenu.brand = tpl end + topmenu.add_link new TplLink("index.html", "Overview") + topmenu.add_link new TplLink("search.html", "Index") return topmenu end @@ -344,7 +350,7 @@ abstract class NitdocPage do if location == null then return null var source = ctx.opt_source.value - if source == null then return "{location.file.filename.simplify_path}" + if source == null then return location.file.filename.simplify_path # THIS IS JUST UGLY ! (but there is no replace yet) var x = source.split_with("%f") source = x.join(location.file.filename.simplify_path) @@ -382,26 +388,30 @@ abstract class NitdocPage article.content = mmodule.tpl_definition # mclassdefs list var intros = mmodule.intro_mclassdefs(ctx.min_visibility).to_a - mainmodule.linearize_mclassdefs(intros) - var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}_intros", "Introduces") - var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) - for mclassdef in intros do - intros_lst.add_li new TplListItem.with_content(mclassdef.tpl_list_item) - end - if not intros_lst.is_empty then - intros_art.content = intros_lst - article.add_child intros_art + if not intros.is_empty then + mainmodule.linearize_mclassdefs(intros) + var intros_art = new TplArticle.with_title("{mmodule.nitdoc_id}_intros", "Introduces") + var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) + for mclassdef in intros do + intros_lst.add_li mclassdef.tpl_list_item + end + if not intros_lst.is_empty then + intros_art.content = intros_lst + article.add_child intros_art + end end var redefs = mmodule.redef_mclassdefs(ctx.min_visibility).to_a - mainmodule.linearize_mclassdefs(redefs) - var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}_redefs", "Redefines") - var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) - for mclassdef in redefs do - redefs_lst.add_li new TplListItem.with_content(mclassdef.tpl_list_item) - end - if not redefs_lst.is_empty then - redefs_art.content = redefs_lst - article.add_child redefs_art + if not redefs.is_empty then + mainmodule.linearize_mclassdefs(redefs) + var redefs_art = new TplArticle.with_title("{mmodule.nitdoc_id}_redefs", "Redefines") + var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) + for mclassdef in redefs do + redefs_lst.add_li mclassdef.tpl_list_item + end + if not redefs_lst.is_empty then + redefs_art.content = redefs_lst + article.add_child redefs_art + end end return article end @@ -425,7 +435,7 @@ abstract class NitdocPage var intros = new TplArticle.with_title("{mclassdef.nitdoc_id}_intros", "Introduces") var intros_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in mclassdef.collect_intro_mpropdefs(ctx.min_visibility) do - intros_lst.add_li new TplListItem.with_content(mpropdef.tpl_list_item) + intros_lst.add_li mpropdef.tpl_list_item end if not intros_lst.is_empty then intros.content = intros_lst @@ -434,7 +444,7 @@ abstract class NitdocPage var redefs = new TplArticle.with_title("{mclassdef.nitdoc_id}_redefs", "Redefines") var redefs_lst = new TplList.with_classes(["list-unstyled", "list-labeled"]) for mpropdef in mclassdef.collect_redef_mpropdefs(ctx.min_visibility) do - redefs_lst.add_li new TplListItem.with_content(mpropdef.tpl_list_item) + redefs_lst.add_li mpropdef.tpl_list_item end if not redefs_lst.is_empty then redefs.content = redefs_lst @@ -499,12 +509,7 @@ class NitdocOverview end end - redef fun tpl_topmenu do - var topmenu = super - topmenu.add_item(new TplLink("#", "Overview"), true) - topmenu.add_item(new TplLink("search.html", "Index"), false) - return topmenu - end + redef fun page_url do return "index.html" # intro text private fun tpl_intro: TplSection do @@ -547,12 +552,7 @@ class NitdocSearch redef fun tpl_title do return "Index" - redef fun tpl_topmenu do - var topmenu = super - topmenu.add_item(new TplLink("index.html", "Overview"), false) - topmenu.add_item(new TplLink("#", "Index"), true) - return topmenu - end + redef fun page_url do return "search.html" redef fun tpl_content do var tpl = new TplSearchPage("search_all") @@ -646,19 +646,17 @@ class NitdocGroup private var sidebar = new TplSidebar redef fun tpl_sidebar do return sidebar - redef fun tpl_title do return "{mgroup.nitdoc_name}" + redef fun tpl_title do return mgroup.nitdoc_name + + redef fun page_url do return mgroup.nitdoc_url redef fun tpl_topmenu do var topmenu = super var mproject = mgroup.mproject - topmenu.add_item(new TplLink("index.html", "Overview"), false) - if mgroup.is_root then - topmenu.add_item(new TplLink("#", "{mproject.nitdoc_name}"), true) - else - topmenu.add_item(new TplLink(mproject.nitdoc_url, "{mproject.nitdoc_name}"), false) - topmenu.add_item(new TplLink("#", "{mgroup.nitdoc_name}"), true) + if not mgroup.is_root then + topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name) end - topmenu.add_item(new TplLink("search.html", "Index"), false) + topmenu.add_link new TplLink(page_url, mproject.nitdoc_name) return topmenu end @@ -678,7 +676,7 @@ class NitdocGroup tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list) end - private fun tpl_sidebar_item(def: MClass): Template do + private fun tpl_sidebar_item(def: MClass): TplListItem do var classes = def.intro.tpl_css_classes.to_a if intros.has(def) then classes.add "intro" @@ -688,7 +686,7 @@ class NitdocGroup var lnk = new Template lnk.add new TplLabel.with_classes(classes) lnk.add def.tpl_link - return lnk + return new TplListItem.with_content(lnk) end # intro text @@ -781,15 +779,14 @@ class NitdocModule private var sidebar = new TplSidebar redef fun tpl_sidebar do return sidebar - redef fun tpl_title do return "{mmodule.nitdoc_name}" + redef fun tpl_title do return mmodule.nitdoc_name + redef fun page_url do return mmodule.nitdoc_url redef fun tpl_topmenu do var topmenu = super var mproject = mmodule.mgroup.mproject - topmenu.add_item(new TplLink("index.html", "Overview"), false) - topmenu.add_item(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false) - topmenu.add_item(new TplLink("#", "{mmodule.nitdoc_name}"), true) - topmenu.add_item(new TplLink("search.html", "Index"), false) + topmenu.add_link new TplLink(mproject.nitdoc_url, mproject.nitdoc_name) + topmenu.add_link new TplLink(page_url, mmodule.nitdoc_name) return topmenu end @@ -809,7 +806,7 @@ class NitdocModule tpl_sidebar.boxes.add new TplSideBox.with_content("All classes", list) end - private fun tpl_sidebar_item(def: MClass): Template do + private fun tpl_sidebar_item(def: MClass): TplListItem do var classes = def.intro.tpl_css_classes.to_a if def.intro_mmodule == mmodule then classes.add "intro" @@ -819,7 +816,7 @@ class NitdocModule var lnk = new Template lnk.add new TplLabel.with_classes(classes) lnk.add def.tpl_link - return lnk + return new TplListItem.with_content(lnk) end # intro text @@ -1018,14 +1015,13 @@ class NitdocClass redef fun tpl_sidebar do return sidebar redef fun tpl_title do return "{mclass.nitdoc_name}{mclass.tpl_signature.write_to_string}" + redef fun page_url do return mclass.nitdoc_url redef fun tpl_topmenu do var topmenu = super var mproject = mclass.intro_mmodule.mgroup.mproject - topmenu.add_item(new TplLink("index.html", "Overview"), false) - topmenu.add_item(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false) - topmenu.add_item(new TplLink("#", "{mclass.nitdoc_name}"), true) - topmenu.add_item(new TplLink("search.html", "Index"), false) + topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}") + topmenu.add_link new TplLink(page_url, mclass.nitdoc_name) return topmenu end @@ -1052,14 +1048,14 @@ class NitdocClass summary.elts.add entry end - private fun tpl_sidebar_item(mprop: MProperty): Template do + private fun tpl_sidebar_item(mprop: MProperty): TplListItem do var classes = mprop.intro.tpl_css_classes.to_a if not mprops2mdefs.has_key(mprop) then classes.add "inherit" var lnk = new Template lnk.add new TplLabel.with_classes(classes) lnk.add mprop.intro.tpl_link - return lnk + return new TplListItem.with_content(lnk) end var defs = mprops2mdefs[mprop] if defs.has(mprop.intro) then @@ -1070,7 +1066,7 @@ class NitdocClass var lnk = new Template lnk.add new TplLabel.with_classes(classes) lnk.add mprop.intro.tpl_anchor - return lnk + return new TplListItem.with_content(lnk) end private fun tpl_intro: TplSection do @@ -1302,17 +1298,6 @@ class NitdocClass return res end - private fun sort_by_public_owner(mmodules: Collection[MModule]): Map[MModule, Set[MModule]] do - var map = new HashMap[MModule, Set[MModule]] - for mmodule in mmodules do - var owner = mmodule - if mmodule.public_owner != null then owner = mmodule.public_owner.as(not null) - if not map.has_key(owner) then map[owner] = new HashSet[MModule] - map[owner].add mmodule - end - return map - end - # Generate dot hierarchy for classes fun tpl_dot(mclasses: Collection[MClass]): nullable TplArticle do var poset = new POSet[MClass] @@ -1379,16 +1364,16 @@ class NitdocProperty return "{mproperty.nitdoc_name}{mproperty.tpl_signature.write_to_string}" end + redef fun page_url do return mproperty.nitdoc_url + redef fun tpl_topmenu do var topmenu = super var mmodule = mproperty.intro_mclassdef.mmodule var mproject = mmodule.mgroup.mproject var mclass = mproperty.intro_mclassdef.mclass - topmenu.add_item(new TplLink("index.html", "Overview"), false) - topmenu.add_item(new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}"), false) - topmenu.add_item(new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}"), false) - topmenu.add_item(new TplLink("#", "{mproperty.nitdoc_name}"), true) - topmenu.add_item(new TplLink("search.html", "Index"), false) + topmenu.add_link new TplLink("{mproject.nitdoc_url}", "{mproject.nitdoc_name}") + topmenu.add_link new TplLink("{mclass.nitdoc_url}", "{mclass.nitdoc_name}") + topmenu.add_link new TplLink(page_url, mproperty.nitdoc_name) return topmenu end diff --git a/src/doc/doc_templates.nit b/src/doc/doc_templates.nit index 7650feb..1d1fb45 100644 --- a/src/doc/doc_templates.nit +++ b/src/doc/doc_templates.nit @@ -25,6 +25,9 @@ class TplPage # Page title in HTML header var title: String is writable, noinit + # Page url + var url: String is writable, noinit + # Directory where css, js and other assets can be found var shareurl: String is writable, noinit @@ -105,7 +108,7 @@ class TplPage add "" add "" add "" - add "" for script in scripts do add script add """