As said in #1895, we need faster parsing of UTF-16 escaping sequences, this PR is the answer.
It makes the runtime of the `large_escaped` benchmark go down from ~5s to ~3.5s, and with valgrind, from 26GIr to 20GIr
Note: based on #1886, only the 4 last commits are of interest here
Pull-Request: #1896
Reviewed-by: Jean Privat <jean@pryen.org>
--- /dev/null
+[submodule "ujson4c"]
+ path = benchmarks/json/thirdparty/ujson4c
+ url = https://github.com/esnme/ujson4c
$ . misc/nit_env.sh install
-More information: <http://www.nitlanguage.org>
+
+Information, contacts and help:
+
+* Website <http://www.nitlanguage.org>
+* Issues <https://github.com/nitlang/nit/issues>
+* Chatroom <https://gitter.im/nitlang/nit>
--- /dev/null
+all:
+ ./bench_json.sh
--- /dev/null
+#!/bin/bash
+# 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.
+
+# Shell script to bench json parsers over different documents
+
+source ../bench_common.sh
+source ../bench_plot.sh
+
+## CONFIGURATION OPTIONS ##
+
+# Default number of times a command must be run with bench_command
+# Can be overrided with 'the option -n'
+count=5
+
+## HANDLE OPTIONS ##
+
+function init_repo()
+{
+ echo "Preparing submodules"
+ git submodule update --init
+ echo "Submodules ready"
+ mkdir -p inputs
+ echo "Preparing data for benchmarks"
+ if [ ! -e inputs/large_escaped.json ]; then
+ echo "Downloading file 1/4"
+ wget -O inputs/large_escaped.json https://github.com/seductiveapps/largeJSON/blob/master/100mb.json?raw=true
+ fi
+ echo "File 1/4 ready"
+ if [ ! -e inputs/magic.json ]; then
+ echo "Downloading file 2/4"
+ wget -O inputs/magic.json http://mtgjson.com/json/AllSets-x.json
+ fi
+ echo "File 2/4 ready"
+ if [ ! -e inputs/big_twitter.json ]; then
+ echo "Downloading file 3/4"
+ wget -O inputs/twitter.json https://github.com/miloyip/nativejson-benchmark/raw/master/data/twitter.json
+ cd inputs
+ ./multiply_twitter.sh
+ rm twitter.json
+ cd ..
+ fi
+ echo "File 3/4 ready"
+ if [ ! -e inputs/big_gov_data.json ]; then
+ echo "Downloading file 4/4"
+ wget -O inputs/gov_data.json https://edg.epa.gov/data.json
+ cd inputs
+ ./multiply_gov.sh
+ rm gov_data.json
+ cd ..
+ fi
+ echo "File 4/4 ready"
+}
+
+function usage()
+{
+ echo "run_bench: ./bench_json.sh [options]"
+ echo " -v: verbose mode"
+ echo " -n count: number of execution for each bar (default: $count)"
+ echo " -h: this help"
+}
+
+stop=false
+while [ "$stop" = false ]; do
+ case "$1" in
+ -v) verbose=true; shift;;
+ -h) usage; exit;;
+ -n) count="$2"; shift; shift;;
+ *) stop=true
+ esac
+done
+
+init_repo
+
+mkdir -p out
+
+echo "Compiling engines"
+
+echo "C JSON Parser"
+
+gcc -O2 -I thirdparty/ujson4c/src -I thirdparty/ujson4c/3rdparty/ thirdparty/ujson4c/3rdparty/ultrajsondec.c scripts/c_parser.c -o scripts/c_parser -lm
+
+echo "Go JSON Parser"
+
+go build -o scripts/json_parse scripts/json_parse.go
+
+echo "Nit/NitCC Parser"
+
+nitc --semi-global scripts/nitcc_parser.nit -o scripts/nitcc_parser
+
+echo "Nit/Ad-Hoc UTF-8 Parser, No Ropes"
+
+nitc --semi-global scripts/nit_adhoc_utf_noropes.nit -o scripts/nit_adhoc_utf_noropes
+
+echo "Nit/Ad-Hoc UTF-8 Parser, With Ropes"
+
+nitc --semi-global scripts/nit_adhoc_utf_ropes.nit -o scripts/nit_adhoc_utf_ropes
+
+declare -a script_names=('C' 'Python 3' 'Python 2' 'Go' 'Nit Ad-hoc UTF-8 No Ropes' 'Nit Ad-hoc UTF-8 + Ropes' 'Ruby ext')
+declare -a script_cmds=('./scripts/c_parser' 'python3 scripts/python.py' 'python2 scripts/python.py' './scripts/json_parse' './scripts/nit_adhoc_utf_noropes' './scripts/nit_adhoc_utf_ropes' 'ruby scripts/json_ext.rb')
+
+for script in `seq 1 ${#script_cmds[@]}`; do
+ echo "Preparing res for ${script_names[$script - 1]}"
+ prepare_res "./out/${script_names[$script - 1]}.dat" "${script_names[$script - 1]}" "${script_names[$script - 1]}"
+ for file in inputs/*.json; do
+ fname=`basename $file .json`
+ bench_command $file "Benching file $file using ${script_cmds[$script - 1]} parser" ${script_cmds[$script - 1]} $file
+ done;
+done;
+
+rm scripts/nitcc_parser
+rm scripts/json_parse
+rm scripts/c_parser
+rm scripts/nit_adhoc_utf_noropes
+rm scripts/nit_adhoc_utf_ropes
+
+plot out/bench_json.gnu
--- /dev/null
+#!/bin/bash
+# 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.
+
+echo "[" > big_gov_data.json
+for i in $(seq 10); do
+ test "$i" != "1" && echo "," >> big_gov_data.json
+ cat gov_data.json >> big_gov_data.json
+done
+echo "]" >> big_gov_data.json
--- /dev/null
+#!/bin/bash
+# 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.
+
+echo "[" > big_twitter.json
+for i in $(seq 100); do
+ test "$i" != "1" && echo "," >> big_twitter.json
+ cat twitter.json >> big_twitter.json
+done
+echo "]" >> big_twitter.json
--- /dev/null
+#include "ujdecode.c"
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+// Gets the byte size of a file
+int file_byte_size(char* path)
+{
+ int f = open(path, O_RDONLY);
+ if(f == -1)
+ return -1;
+ struct stat s;
+ int ln = 0;
+ if(!fstat(f, &s))
+ ln = s.st_size;
+ close(f);
+ return ln;
+}
+
+int main(int argc, char** argv)
+{
+ if(argc == 1) {
+ printf("Usage: ./c_parser file\n");
+ exit(1);
+ }
+
+ int fl_sz = file_byte_size(argv[1]);
+ char* input = malloc(fl_sz);
+
+ FILE* ifl = fopen(argv[1], "r");
+ if(ifl == NULL) {
+ printf("Error: cannot read file %s, are you sure permissions are set correctly ?\n", argv[1]);
+ exit(2);
+ }
+ int rd = fread(input, 1, fl_sz, ifl);
+ assert(rd == fl_sz);
+
+ void *state;
+
+ UJObject obj = UJDecode(input, fl_sz, NULL, &state);
+
+ free(input);
+ UJFree(state);
+}
--- /dev/null
+require 'json/ext'
+
+txt = IO.read(ARGV.first)
+my_hash = JSON.parse(txt)
--- /dev/null
+package main
+
+import "io/ioutil"
+import "encoding/json"
+import "os"
+import "fmt"
+
+func main() {
+ if len(os.Args) == 1 {
+ fmt.Println("Usage ./json_parse file")
+ os.Exit(-1)
+ }
+ dat, err := ioutil.ReadFile(os.Args[1])
+ if err != nil { panic(err) }
+
+ var obj interface{}
+
+ jsonerr := json.Unmarshal(dat, &obj)
+ if jsonerr != nil { panic(jsonerr) }
+}
--- /dev/null
+require 'json/pure'
+
+txt = IO.read(ARGV.first)
+my_hash = JSON.parse(txt)
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json::string_parser
+
+var text = args.first.to_path.read_all_bytes.to_s
+var json = text.parse_json
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json::string_parser
+
+var f = new FileReader.open(args.first)
+
+var text = f.read_all
+f.close
+var json = text.parse_json
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+
+var text = args.first.to_path.read_all
+var json = text.parse_json
--- /dev/null
+import sys
+import json
+
+json_data=open(sys.argv[1]).read()
+data = json.loads(json_data)
--- /dev/null
+Subproject commit e14f3fd5207fe30d1bdea723f260609e69d1abfa
return s.plain_to_s
end
+ # Return the Levenshtein distance between two strings
+ #
+ # ~~~
+ # assert "abcd".levenshtein_distance("abcd") == 0
+ # assert "".levenshtein_distance("abcd") == 4
+ # assert "abcd".levenshtein_distance("") == 4
+ # assert "abcd".levenshtein_distance("xyz") == 4
+ # assert "abcd".levenshtein_distance("xbdy") == 3
+ # ~~~
+ fun levenshtein_distance(other: String): Int
+ do
+ var slen = self.length
+ var olen = other.length
+
+ # fast cases
+ if slen == 0 then return olen
+ if olen == 0 then return slen
+ if self == other then return 0
+
+ # previous row of distances
+ var v0 = new Array[Int].with_capacity(olen+1)
+
+ # current row of distances
+ var v1 = new Array[Int].with_capacity(olen+1)
+
+ for j in [0..olen] do
+ # prefix insert cost
+ v0[j] = j
+ end
+
+ for i in [0..slen[ do
+
+ # prefix delete cost
+ v1[0] = i + 1
+
+ for j in [0..olen[ do
+ # delete cost
+ var cost1 = v1[j] + 1
+ # insert cost
+ var cost2 = v0[j + 1] + 1
+ # same char cost (+0)
+ var cost3 = v0[j]
+ # change cost
+ if self[i] != other[j] then cost3 += 1
+ # keep the min
+ v1[j+1] = cost1.min(cost2).min(cost3)
+ end
+
+ # Switch columns:
+ # * v1 become v0 in the next iteration
+ # * old v0 is reused as the new v1
+ var tmp = v1
+ v1 = v0
+ v0 = tmp
+ end
+
+ return v0[olen]
+ end
+
# Copies `n` bytes from `self` at `src_offset` into `dest` starting at `dest_offset`
#
# Basically a high-level synonym of NativeString::copy_to
# This is used for the legacy FFI
fun add_extern(mmodule: MModule)
do
- var file = mmodule.location.file.filename
+ var file = mmodule.filepath
file = file.strip_extension(".nit")
var tryfile = file + ".nit.h"
if tryfile.file_exists then
redef fun make_tag(v)
do
var res = super
- if not parent isa ADoc then
+ if is_loose then
res.add_class("nc_c")
end
return res
end
redef class MModule
- # The path of the module source
- var filepath: nullable String = null
-
# Force the parsing of the module using `modelbuilder`.
#
# If the module was already parsed, the existing ASI is returned.
# build the mmodule
nmodule.mmodule = self
+ self.location = nmodule.location
modelbuilder.build_a_mmodule(mgroup, nmodule)
modelbuilder.parsed_modules.add self
# The group of module in the package if any
var mgroup: nullable MGroup
+ # The path of the module source, if any
+ var filepath: nullable String = null is writable
+
# The package of the module if any
# Safe alias for `mgroup.mpackage`
fun mpackage: nullable MPackage
redef var name: String
# The origin of the definition
- var location: Location
+ var location: Location is writable
# Alias for `name`
redef fun to_s do return self.name
end
end
-# An OrderedTree that can be easily refined for display purposes
+# An OrderedTree bound to MEntity.
+#
+# We introduce a new class so it can be easily refined by tools working
+# with a Model.
+class MEntityTree
+ super OrderedTree[MEntity]
+end
+
+# A MEntityTree borned to MConcern.
+#
+# TODO remove when nitdoc is fully merged with model_collect
class ConcernsTree
super OrderedTree[MConcern]
end
# The short name of the class
# In Nit, the name of a class cannot evolve in refinements
- redef var name: String
+ redef var name
# The canonical name of the class
#
# Internal name combining the module and the class
# Example: "mymodule#MyClass"
- redef var to_s: String is noinit
+ redef var to_s is noinit
init
do
# The short-name of the class, then the full-name of each type arguments within brackets.
# Example: `"Map[String, List[Int]]"`
- redef var to_s: String is noinit
+ redef var to_s is noinit
# The full-name of the class, then the full-name of each type arguments within brackets.
# Example: `"core::Map[core::String, core::List[core::Int]]"`
return res.to_s
end
- redef var need_anchor: Bool is noinit
+ redef var need_anchor is noinit
redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
do
self.to_s = "nullable {mtype}"
end
- redef var to_s: String is noinit
+ redef var to_s is noinit
redef var full_name is lazy do return "nullable {mtype.full_name}"
# The is only one null type per model, see `MModel::null_type`.
class MNullType
super MType
- redef var model: Model
+ redef var model
redef fun to_s do return "null"
redef fun full_name do return "null"
redef fun c_name do return "null"
# Semantically it is the singleton `null.as_notnull`.
class MBottomType
super MType
- redef var model: Model
+ redef var model
redef fun to_s do return "bottom"
redef fun full_name do return "bottom"
redef fun c_name do return "bottom"
super MEntity
# The name of the parameter
- redef var name: String
+ redef var name
# The static type of the parameter
var mtype: MType
var intro_mclassdef: MClassDef
# The (short) name of the property
- redef var name: String
+ redef var name
# The canonical name of the property.
#
# Internal name combining the module, the class and the property
# Example: "mymodule#MyClass#mymethod"
- redef var to_s: String is noinit
+ redef var to_s is noinit
# Is self the definition that introduce the property?
fun is_intro: Bool do return isset mproperty._intro and mproperty.intro == self
# Note this class is basically an enum.
# FIXME: use a real enum once user-defined enums are available
class MClassKind
- redef var to_s: String
+ redef var to_s
# Is a constructor required?
var need_init: Bool
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module model_views
+
+import model_visitor
+
+# Provide a configurable view to a model.
+#
+# This can be useful when you need to filter some mentities from a model
+# like private or fictive.
+#
+# TODO doc usage
+class ModelView
+ super ModelVisitor
+
+ # The model to view through `self`.
+ var model: Model
+
+ # MPackages visible through `self`.
+ var mpackages: Set[MPackage] is lazy do
+ var mpackages = new HashSet[MPackage]
+ for mpackage in model.mpackages do
+ if not accept_mentity(mpackage) then continue
+ mpackages.add mpackage
+ end
+ return mpackages
+ end
+
+ # MGroups visible through `self`.
+ var mgroups: Set[MGroup] is lazy do
+ var mgroups = new HashSet[MGroup]
+ for mpackage in mpackages do
+ for mgroup in mpackage.mgroups do
+ if not accept_mentity(mgroup) then continue
+ mgroups.add mgroup
+ end
+ end
+ return mgroups
+ end
+
+ # MModules visible through `self`.
+ var mmodules: Set[MModule] is lazy do
+ var mmodules = new HashSet[MModule]
+ for mmodule in model.mmodules do
+ if not accept_mentity(mmodule) then continue
+ mmodules.add mmodule
+ end
+ return mmodules
+ end
+
+ # MClasses visible through `self`.
+ var mclasses: Set[MClass] is lazy do
+ var mclasses = new HashSet[MClass]
+ for mclass in model.mclasses do
+ if not accept_mentity(mclass) then continue
+ mclasses.add mclass
+ end
+ return mclasses
+ end
+
+ # MClassDefs visible through `self`.
+ var mclassdefs: Set[MClassDef] is lazy do
+ var mclassdefs = new HashSet[MClassDef]
+ for mclass in mclasses do
+ for mclassdef in mclass.mclassdefs do
+ if not accept_mentity(mclassdef) then continue
+ mclassdefs.add mclassdef
+ end
+ end
+ return mclassdefs
+ end
+
+ # MProperties visible through `self`.
+ var mproperties: Set[MProperty] is lazy do
+ var mproperties = new HashSet[MProperty]
+ for mproperty in model.mproperties do
+ if not accept_mentity(mproperty) then continue
+ mproperties.add mproperty
+ end
+ return mproperties
+ end
+
+ # MPropdefs visible through `self`.
+ var mpropdefs: Set[MPropDef] is lazy do
+ var mpropdefs = new HashSet[MPropDef]
+ for mproperty in mproperties do
+ for mpropdef in mproperty.mpropdefs do
+ if not accept_mentity(mpropdef) then continue
+ mpropdefs.add mpropdef
+ end
+ end
+ return mpropdefs
+ end
+
+ # Lists all MEntities visible through `self`.
+ var mentities: Set[MEntity] is lazy do
+ var res = new HashSet[MEntity]
+ res.add_all mpackages
+ res.add_all mgroups
+ res.add_all mmodules
+ res.add_all mclasses
+ res.add_all mclassdefs
+ res.add_all mproperties
+ res.add_all mpropdefs
+ return res
+ end
+
+ private fun init_visitor(v: ModelVisitor) do
+ v.min_visibility = self.min_visibility
+ v.include_fictive = self.include_fictive
+ v.include_empty_doc = self.include_empty_doc
+ v.include_attribute = self.include_attribute
+ v.include_test_suite = self.include_test_suite
+ end
+
+ # Searches MEntities that match `name`.
+ fun mentities_by_name(name: String): Array[MEntity] do
+ var res = new Array[MEntity]
+ for mentity in mentities do if mentity.name == name then res.add mentity
+ return res
+ end
+
+ # Looks up a MEntity by its full `namespace`.
+ #
+ # Usefull when `mentities_by_name` returns conflicts.
+ #
+ # Namespaces must be of the form `package::core::module::Class::prop`.
+ fun mentities_by_namespace(namespace: String): Array[MEntity] do
+ var v = new LookupNamespaceVisitor(namespace)
+ init_visitor(v)
+ for mpackage in mpackages do
+ v.enter_visit(mpackage)
+ end
+ return v.results
+ end
+
+ # Build an concerns tree with from `self`
+ fun to_tree: MEntityTree do
+ var v = new ModelTreeVisitor
+ init_visitor(v)
+ for mpackage in mpackages do
+ v.enter_visit(mpackage)
+ end
+ return v.tree
+ end
+end
+
+class LookupNamespaceVisitor
+ super ModelVisitor
+
+ var namespace: String
+
+ private var parts: Array[String] is lazy do return namespace.split_with("::")
+
+ var results = new Array[MEntity]
+
+ redef fun visit(mentity) do mentity.accept_namespace_visitor(self)
+end
+
+class ModelTreeVisitor
+ super ModelVisitor
+
+ var tree = new MEntityTree
+
+ redef fun visit(mentity) do mentity.accept_tree_visitor(self)
+end
+
+redef class MEntity
+
+ # Get a public view of the model
+ fun public_view: ModelView do
+ var view = new ModelView(self.model)
+ view.min_visibility = public_visibility
+ return view
+ end
+
+ # Get a public view of the model
+ fun protected_view: ModelView do
+ var view = new ModelView(self.model)
+ view.min_visibility = protected_visibility
+ return view
+ end
+
+ # Get a public view of the model
+ fun private_view: ModelView do
+ var view = new ModelView(self.model)
+ view.min_visibility = private_visibility
+ return view
+ end
+
+ private fun accept_namespace_visitor(v: LookupNamespaceVisitor) do
+ if v.parts.is_empty then return
+ if name != v.parts.first then return
+ v.parts.shift
+ if v.parts.is_empty then
+ v.results.add self
+ return
+ end
+ visit_all(v)
+ end
+
+ private fun accept_tree_visitor(v: ModelTreeVisitor) do end
+end
+
+redef class MPackage
+ redef fun accept_tree_visitor(v) do
+ v.tree.add(null, self)
+ visit_all(v)
+ end
+end
+
+redef class MGroup
+ redef fun accept_tree_visitor(v) do
+ var parent = self.parent
+ if parent != null then
+ v.tree.add(parent, self)
+ else
+ v.tree.add(mpackage, self)
+ end
+ visit_all(v)
+ end
+end
+
+redef class MModule
+ redef fun accept_tree_visitor(v) do
+ v.tree.add(mgroup, self)
+ visit_all(v)
+ end
+end
+
+redef class MClass
+ # We don't want to collect classes from full namespace.
+ redef fun accept_namespace_visitor(v) do end
+end
+
+redef class MClassDef
+ redef fun accept_tree_visitor(v) do
+ v.tree.add(mmodule, self)
+ visit_all(v)
+ end
+end
+
+redef class MProperty
+ # We don't want to collect properties from full namespace.
+ redef fun accept_namespace_visitor(v) do end
+end
+
+redef class MPropDef
+ redef fun accept_tree_visitor(v) do
+ v.tree.add(mclassdef, self)
+ visit_all(v)
+ end
+end
# If `e` is null, nothing is done.
fun enter_visit(e: nullable MEntity) do
if e == null then return
- if e.is_fictive and not include_fictive then return
+ if not accept_mentity(e) then return
var old_entity = current_entity
current_entity = e
visit(e)
# visibility level will be visited.
var min_visibility: nullable MVisibility = null is writable
- # Is `visibility` acceptable with regard to `min_visibility`?
- private fun accept_visitibily(visibility: MVisibility): Bool
- do
- var min = min_visibility
- return min == null or min <= visibility
+ # Can we accept this `mentity` in the view regarding its visibility?
+ fun accept_visibility(mentity: MEntity): Bool do
+ return mentity.accept_visibility(min_visibility)
end
# Include fictive entities?
#
# By default, fictive entities (see `MEntity::is_fictive`) are not visited.
var include_fictive = false is writable
+
+ # Can we accept this `mentity` in the view regarding its fictivity?
+ fun accept_fictive(mentity: MEntity): Bool do
+ if include_fictive then return true
+ return not mentity.is_fictive
+ end
+
+ # Should we accept mentities with empty documentation?
+ #
+ # Default is `true`.
+ var include_empty_doc = true is writable
+
+ # Can we accept this `mentity` regarding its documentation?
+ fun accept_empty_doc(mentity: MEntity): Bool do
+ if include_empty_doc then return true
+ return mentity.mdoc != null
+ end
+
+ # Should we accept nitunit test suites?
+ #
+ # Default is `false`.
+ var include_test_suite = false is writable
+
+ # Can we accept this `mentity` regarding its test suite status?
+ fun accept_test_suite(mentity: MEntity): Bool do
+ if include_test_suite then return true
+ if not mentity isa MModule then return true
+ return not mentity.is_test_suite
+ end
+
+ # Should we accept `MAttribute` instances?
+ #
+ # Default is `true`.
+ var include_attribute = true is writable
+
+ # Can we accept this `mentity` regarding its type?
+ fun accept_attribute(mentity: MEntity): Bool do
+ if include_attribute then return true
+ if mentity isa MAttribute then return false
+ if mentity isa MAttributeDef then return false
+ return true
+ end
+
+ # Should we accept this `mentity` from the view?
+ fun accept_mentity(mentity: MEntity): Bool do
+ if not accept_visibility(mentity) then return false
+ if not accept_fictive(mentity) then return false
+ if not accept_empty_doc(mentity) then return false
+ if not accept_test_suite(mentity) then return false
+ if not accept_attribute(mentity) then return false
+ return true
+ end
+
end
redef class MEntity
#
# See the specific implementation in the subclasses.
fun visit_all(v: ModelVisitor) do end
+
+ private fun accept_visibility(min_visibility: nullable MVisibility): Bool do return true
end
redef class Model
# On class importation, nothing is visited (the `MClass` and the `MClassDef` are visited in imported modules).
redef fun visit_all(v) do
for x in mclassdefs do
- if not v.accept_visitibily(x.mclass.visibility) then return
if x.is_intro then v.enter_visit(x.mclass)
v.enter_visit(x)
end
end
end
+redef class MClass
+ redef fun accept_visibility(min_visibility) do
+ if min_visibility == null then return true
+ return visibility >= min_visibility
+ end
+end
+
redef class MClassDef
# Visit all the classes and class definitions of the module.
#
# On property inheritance, nothing is visited (the `MProperty` and the `MPropDef` are visited in inherited classes).
redef fun visit_all(v) do
for x in mpropdefs do
- if not v.accept_visitibily(x.mproperty.visibility) then return
if x.is_intro then v.enter_visit(x.mproperty)
v.enter_visit(x)
end
end
+
+ redef fun accept_visibility(min_visibility) do
+ if min_visibility == null then return true
+ return mclass.visibility >= min_visibility
+ end
+end
+
+redef class MProperty
+ redef fun accept_visibility(min_visibility) do
+ if min_visibility == null then return true
+ return visibility >= min_visibility
+ end
+end
+
+redef class MPropDef
+ redef fun accept_visibility(min_visibility) do
+ if min_visibility == null then return true
+ return mproperty.visibility >= min_visibility
+ end
end
super ModelVisitor
redef fun visit(e) do
- if not doc_only or e.mdoc != null then
- cpt.inc(e.class_name)
- end
+ cpt.inc(e.class_name)
e.visit_all(self)
end
# Counter of visited entities (by classnames)
var cpt = new Counter[String]
-
- # Do the visitor only count entities with a documentation?
- var doc_only = false
end
# The body of the specific work.
print "All entities, including fictive ones:"
var v = new TestModelVisitor
+ v.min_visibility = private_visibility
v.include_fictive = true
v.enter_visit(model)
v.cpt.print_elements(10)
print "All entities:"
v = new TestModelVisitor
+ v.min_visibility = private_visibility
v.enter_visit(model)
v.cpt.print_elements(10)
print "\nAll documented non-private entities:"
v = new TestModelVisitor
v.min_visibility = protected_visibility
- v.doc_only = true
+ v.include_empty_doc = false
+ v.enter_visit(model)
+ v.cpt.print_elements(10)
+
+ print "\nAll public entities:"
+ v = new TestModelVisitor
+ v.min_visibility = public_visibility
v.enter_visit(model)
v.cpt.print_elements(10)
+
+ print "\nAll documented public entities:"
+ v = new TestModelVisitor
+ v.min_visibility = public_visibility
+ v.include_empty_doc = false
+ v.enter_visit(model)
+ v.cpt.print_elements(10)
+
end
end
var opts = new Array[String]
if mmodule != null then
- opts.add "-I {mmodule.location.file.filename.dirname}"
+ opts.add "-I {mmodule.filepath.dirname}"
end
var cmd = "{nitc} --ignore-visibility --no-color '{file}' {opts.join(" ")} >'{file}.out1' 2>&1 </dev/null -o '{file}.bin'"
var res = sys.system(cmd)
\e[1m\e[32mP\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
\e[1m\e[30mbase_simple3\e[m\e[m
package base_simple3
- \e[30mbase_simple3.nit\e[m
+ \e[30mbase_simple3.nit:17,1--66,13\e[m
\e[1m\e[32mG\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
\e[1m\e[30mbase_simple3\e[m\e[m
group base_simple3
- \e[30mbase_simple3.nit\e[m
+ \e[30mbase_simple3.nit:17,1--66,13\e[m
\e[1m\e[32mM\e[m\e[m \e[1m\e[34mbase_simple3\e[m\e[m
\e[1m\e[30mbase_simple3::base_simple3\e[m\e[m
module base_simple3
- \e[30mbase_simple3.nit\e[m
+ \e[30mbase_simple3.nit:17,1--66,13\e[m
All non-private entities:
list:
- MMethodDef: 8 (24.24%)
- MMethod: 7 (21.21%)
- MClassDef: 7 (21.21%)
- MClass: 7 (21.21%)
- MModule: 1 (3.03%)
- MGroup: 1 (3.03%)
- MPackage: 1 (3.03%)
- Model: 1 (3.03%)
+ MMethodDef: 17 (34.00%)
+ MMethod: 15 (30.00%)
+ MClassDef: 7 (14.00%)
+ MClass: 7 (14.00%)
+ MModule: 1 (2.00%)
+ MGroup: 1 (2.00%)
+ MPackage: 1 (2.00%)
+ Model: 1 (2.00%)
All documented non-private entities:
list:
+
+All public entities:
+ list:
+ MMethodDef: 14 (31.81%)
+ MMethod: 12 (27.27%)
+ MClassDef: 7 (15.90%)
+ MClass: 7 (15.90%)
+ MModule: 1 (2.27%)
+ MGroup: 1 (2.27%)
+ MPackage: 1 (2.27%)
+ Model: 1 (2.27%)
+
+All documented public entities:
+ list: