It is less and less an example but nitlight_as_a_service now features a client-side editor (http://codemirror.net/) and update the page with some crappy ajax code.
http://test.nitlanguage.org/
Pull-Request: #2197
# Remove their transitions
for s in bads do
- for t in s.ins do t.delete
- for t in s.outs do t.delete
+ for t in s.ins.to_a do t.delete
+ for t in s.outs.to_a do t.delete
end
# Keep only the good stuff
# REQUIRE: self is a DFA
fun to_minimal_dfa: Automaton
do
+ assert_valid
+
trim
+ # Graph of known distinct states.
var distincts = new HashMap[State, Set[State]]
for s in states do
distincts[s] = new HashSet[State]
end
- # split accept states
+ # split accept states.
+ # An accept state is distinct with a non accept state.
for s1 in states do
for s2 in states do
if distincts[s1].has(s2) then continue
distincts[s2].add(s1)
continue
end
- if tags[s1] != tags[s2] then
+ if tags.get_or_null(s1) != tags.get_or_null(s2) then
distincts[s1].add(s2)
distincts[s2].add(s1)
continue
end
end
+ # Fixed point algorithm.
+ # * Get 2 states s1 and s2 not yet distinguished.
+ # * Get a symbol w.
+ # * If s1.trans(w) and s2.trans(w) are distinguished, then
+ # distinguish s1 and s2.
var changed = true
- var ints = new Array[Int]
+ var ints = new Array[Int] # List of symbols to check
while changed do
changed = false
for s1 in states do for s2 in states do
if distincts[s1].has(s2) then continue
+
+ # The transitions use intervals. Therefore, for the states s1 and s2,
+ # we need to check only the meaningful symbols. They are the `first`
+ # symbol of each interval and the first one after the interval (`last+1`).
ints.clear
+ # Check only `s1`; `s2` will be checked later when s1 and s2 are switched.
for t in s1.outs do
var sym = t.symbol
assert sym != null
ints.add sym.first
var l = sym.last
- if l != null then ints.add l
+ if l != null then ints.add l + 1
end
+
+ # Check each symbol
for i in ints do
var ds1 = s1.trans(i)
var ds2 = s2.trans(i)
- if ds1 == null and ds2 == null then continue
+ if ds1 == ds2 then continue
if ds1 != null and ds2 != null and not distincts[ds1].has(ds2) then continue
distincts[s1].add(s2)
distincts[s2].add(s1)
end
end
+ # We need to unify not-distinguished states.
+ # Just add an epsilon-transition and DFAize the automaton.
for s1 in states do for s2 in states do
if distincts[s1].has(s2) then continue
s1.add_trans(s2, null)
return to_dfa
end
+ # Assert that `self` is a valid automaton or abort
+ fun assert_valid
+ do
+ assert states.has(start)
+ assert states.has_all(accept)
+ for s in states do
+ for t in s.outs do assert states.has(t.to)
+ for t in s.ins do assert states.has(t.from)
+ end
+ assert states.has_all(tags.keys)
+ for t, ss in retrotags do
+ assert states.has_all(ss)
+ end
+ end
+
# Produce a graphvis file for the automaton
fun to_dot(filepath: String)
do
# note: the DFA is not minimized.
fun to_dfa: Automaton
do
+ assert_valid
+
trim
var dfa = new Automaton.empty
{plus:} re3 '+' |
{shortest:} 'Shortest' '(' re ')' |
{longest:} 'Longest' '(' re ')' |
+ {prefixes:} 'Prefixes' '(' re ')' |
{id:} id |
{par:} '(' re ')' |
{class:} text '.' '.' text |
var t_except = new Token("except")
var t_shortest = new Token("shortest")
var t_longest = new Token("longest")
+var t_prefixes = new Token("prefixes")
var t_ch_dec = new Token("ch_dec")
var t_ch_hex = new Token("ch_hex")
g.tokens.add_all([t_opar,
t_except,
t_shortest,
t_longest,
+ t_prefixes,
t_ch_dec,
t_ch_hex])
p_re3.new_alt("re_plus", p_re3, t_plus)
p_re3.new_alt("re_shortest", t_shortest, t_opar, p_re, t_cpar)
p_re3.new_alt("re_longest", t_longest, t_opar, p_re, t_cpar)
+p_re3.new_alt("re_prefixes", t_prefixes, t_opar, p_re, t_cpar)
p_re3.new_alt("re_par", t_opar, p_re, t_cpar)
p_re3.new_alt("re_class", p_text, t_dot, t_dot, p_text)
p_re3.new_alt("re_openclass", p_text, t_dot, t_dot, t_dot)
end
end
+redef class Nre_prefixes
+ redef fun make_rfa
+ do
+ var a = children[2].make_rfa
+ a.trim
+ a.accept.add_all a.states
+ return a
+ end
+end
+
redef class Nre_conc
redef fun make_rfa
do
--- /dev/null
+Grammar x;
+
+Lexer
+ m = 'abcd' | 'x'* 'y'+ 'z'?;
+ pm = Prefixes(m) Except '';
+ err = ('a'..'z') Except pm;
+
+blank = #10 | #13 | #32;
+Parser
+Ignored blank;
+
+ s = p+;
+ p = pm | err;
--- /dev/null
+Start
+ s
+ Nodes[Np]
+ p_0
+ pm@(1:1-1:2)='a'
+ p_1
+ err@(1:3-1:4)='b'
+ p_0
+ pm@(1:5-1:7)='ab'
+ p_0
+ pm@(1:8-1:11)='abc'
+ p_0
+ pm@(1:12-1:16)='abcd'
+ p_0
+ pm@(1:17-1:21)='abcd'
+ p_1
+ err@(1:21-1:22)='e'
+ p_0
+ pm@(1:23-1:24)='a'
+ p_0
+ pm@(1:24-1:26)='ab'
+ p_1
+ err@(1:26-1:27)='b'
+ p_1
+ err@(1:27-1:28)='c'
+ p_1
+ err@(1:28-1:29)='c'
+ p_1
+ err@(1:29-1:30)='d'
+ p_1
+ err@(1:30-1:31)='d'
+ p_0
+ pm@(2:1-2:2)='x'
+ p_0
+ pm@(2:3-2:4)='y'
+ p_1
+ err@(2:5-2:6)='z'
+ p_0
+ pm@(2:7-2:10)='xyz'
+ p_0
+ pm@(2:11-2:12)='x'
+ p_1
+ err@(2:12-2:13)='z'
+ p_0
+ pm@(2:14-2:16)='xy'
+ p_0
+ pm@(2:17-2:19)='yz'
+ p_0
+ pm@(2:20-2:25)='xxyyz'
+ p_1
+ err@(2:25-2:26)='z'
+ Eof@(3:1-3:1)=''
<body>
<nav class='navbar navbar-default navbar-fixed-top'>
<div class='container-fluid'>
- <div class='col-xs-3 navbar-header'>
- <a class='navbar-brand' ng-href='/'>Nitdoc</a>
+ <div class='col-xs-3'>
+ <div class='navbar-header'>
+ <a class='navbar-brand' ng-href='/'>Nitdoc</a>
+ </div>
+ <ul class="nav navbar-nav">
+ <li><a href="/docdown?edit=true">DocDown</a></li>
+ </ul>
</div>
<div class='col-xs-7'>
<form ng-controller='SearchCtrl as searchCtrl' >
<script src='/javascripts/entities.js'></script>
<script src='/javascripts/ui.js'></script>
<script src='/javascripts/index.js'></script>
+ <script src='/javascripts/docdown.js'></script>
</body>
</html>
--- /dev/null
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.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.
+ */
+
+(function() {
+ angular
+ .module('docdown', ['model', 'ngSanitize'])
+
+ .controller('DocdownCtrl', ['$routeParams', '$sce', '$scope', '$location', 'DocDown', function($routeParams, $sce, $scope, $location, DocDown) {
+
+ this.updateSnippet = function() {
+ this.updateLink();
+ this.updateHtml();
+ }
+
+ this.updateLink = function() {
+ $scope.link = $location.protocol()+ '://' + $location.host() + ':' +
+ $location.port() + $location.path() + '?snippet=' +
+ encodeURIComponent(btoa($scope.markdown));
+ }
+
+ this.updateHtml = function() {
+ DocDown.postMarkdown($scope.markdown,
+ function(data) {
+ $scope.html = $sce.trustAsHtml(data);
+ }, function(err) {
+ $scope.error = err;
+ });
+ };
+
+ this.editMode = function(isEdit) {
+ $scope.edit = isEdit;
+ }
+
+ $scope.markdown = 'Type some markdown...';
+ if($location.search().snippet) {
+ $scope.markdown = atob($location.search().snippet);
+ }
+ $scope.edit = false;
+ if($location.search().edit) {
+ $scope.edit = Boolean($location.search().edit);
+ }
+
+ this.updateSnippet();
+ }])
+})();
},
}
}])
+
+ .factory('DocDown', [ '$http', function($http) {
+ return {
+ postMarkdown: function(md, cb, cbErr) {
+ $http.post(apiUrl + '/docdown', md)
+ .success(cb)
+ .error(cbErr);
+ }
+ }
+ }])
})();
*/
(function() {
- angular.module('nitweb', ['ngRoute', 'ngSanitize', 'angular-loading-bar', 'entities', 'index'])
+ angular.module('nitweb', ['ngRoute', 'ngSanitize', 'angular-loading-bar', 'entities', 'docdown', 'index'])
.config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
cfpLoadingBarProvider.includeSpinner = false;
}])
controller: 'IndexCtrl',
controllerAs: 'indexCtrl'
})
+ .when('/docdown', {
+ templateUrl: 'views/docdown.html',
+ controller: 'DocdownCtrl',
+ controllerAs: 'docdownCtrl'
+ })
.when('/doc/:id', {
templateUrl: 'views/doc.html',
controller: 'EntityCtrl',
--- /dev/null
+<div class='container-fluid'>
+ <div class='page-header'>
+ <h2>Docdown snippets</h2>
+ <p class='text-muted'>Sharable documentation snippets.</p>
+ <div class="input-group">
+ <span ng-if='edit' class="input-group-btn">
+ <button class='btn btn-success' ng-click='docdownCtrl.editMode(false)'>
+ <span class='glyphicon glyphicon-link' /> View
+ </button>
+ </span>
+ <span ng-if='!edit' class="input-group-btn">
+ <button class='btn btn-success' ng-click='docdownCtrl.editMode(true)'>
+ <span class='glyphicon glyphicon-edit' /> Edit
+ </button>
+ </span>
+ <input class='form-control' type='text' ng-model='link' />
+ </div>
+ </div>
+ <div class='row'>
+ <div ng-show='edit' class='col-xs-6'>
+ <div class='card'>
+ <textarea ng-model='markdown' ng-model-options='{ debounce: 100 }' ng-change='docdownCtrl.updateSnippet()' class='form-control' rows='20'></textarea>
+ </div>
+ </div>
+ <div ng-class='edit ? "col-xs-6" : "col-xs-12"'>
+ <div class='card'>
+ <div class='card-body nitdoc'>
+ <div ng-bind-html='html' />
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
# * `nitdoc` wikilinks like `[[doc: MEntity::name]]`
module doc_commands
-import doc_base
-
# A command aimed at a documentation tool like `nitdoc` or `nitx`.
#
# `DocCommand` are generally of the form `command: args`.
return new CallCommand(command_string)
else if command_string.has_prefix("code:") then
return new CodeCommand(command_string)
+ else if command_string.has_prefix("graph:") then
+ return new GraphCommand(command_string)
end
return new UnknownCommand(command_string)
end
class CodeCommand
super AbstractDocCommand
end
+
+# A `DocCommand` that display an graph for a `MEntity`.
+#
+# Syntax:
+# * `graph: MEntity::name`
+class GraphCommand
+ super AbstractDocCommand
+end
use("/defs/:id", new APIEntityDefs(model, mainmodule))
use("/inheritance/:id", new APIEntityInheritance(model, mainmodule))
use("/graph/", new APIGraphRouter(model, mainmodule))
+ use("/docdown/", new APIDocdown(model, mainmodule, modelbuilder))
end
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Nitdoc specific Markdown format handling for Nitweb
+module api_docdown
+
+import api_graph
+intrude import doc_down
+intrude import markdown::wikilinks
+import doc_commands
+
+# Docdown handler accept docdown as POST data and render it as HTML
+class APIDocdown
+ super APIHandler
+
+ # Modelbuilder used by the commands
+ var modelbuilder: ModelBuilder
+
+ # Specific Markdown processor to use within Nitweb
+ var md_processor: MarkdownProcessor is lazy do
+ var proc = new MarkdownProcessor
+ proc.emitter.decorator = new NitwebDecorator(view, modelbuilder)
+ return proc
+ end
+
+ redef fun post(req, res) do
+ res.html md_processor.process(req.body)
+ end
+end
+
+# Specific Markdown decorator for Nitweb
+#
+# We reuse all the implementation of the NitdocDecorator and add the wikilinks handling.
+class NitwebDecorator
+ super NitdocDecorator
+
+ # View used by wikilink commands to find model entities
+ var view: ModelView
+
+ # Modelbuilder used to access code
+ var modelbuilder: ModelBuilder
+
+ redef fun add_wikilink(v, token) do
+ var link = token.link
+ if link == null then return
+ var cmd = new DocCommand(link.write_to_string)
+ cmd.render(v, token, view)
+ end
+end
+
+# Same as `InlineDecorator` but with wikilink commands handling
+class NitwebInlineDecorator
+ super InlineDecorator
+
+ # View used by wikilink commands to find model entities
+ var view: ModelView
+
+ # Modelbuilder used to access code
+ var modelbuilder: ModelBuilder
+
+ redef fun add_wikilink(v, token) do
+ var link = token.link
+ if link == null then return
+ var cmd = new DocCommand(link.write_to_string)
+ cmd.render(v, token, view)
+ end
+end
+
+redef interface DocCommand
+
+ # Emit the HTML related to the execution of this doc command
+ fun render(v: MarkdownEmitter, token: TokenWikiLink, model: ModelView) do
+ write_error(v, "Not yet implemented command `{token.link or else "null"}`")
+ end
+
+ # Find the MEntity ` with `full_name`.
+ fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
+ if full_name == null then return null
+ return model.mentity_by_full_name(full_name.from_percent_encoding)
+ end
+
+ # Write a warning in the output
+ fun write_warning(v: MarkdownEmitter, text: String) do
+ v.emit_text "<p class='text-warning'>Warning: {text}</p>"
+ end
+
+ # Write an error in the output
+ fun write_error(v: MarkdownEmitter, text: String) do
+ v.emit_text "<p class='text-danger'>Error: {text}</p>"
+ end
+
+ # Write a link to a mentity in the output
+ fun write_mentity_link(v: MarkdownEmitter, mentity: MEntity) do
+ var link = mentity.web_url
+ var name = mentity.name
+ var mdoc = mentity.mdoc_or_fallback
+ var comment = null
+ if mdoc != null then comment = mdoc.synopsis
+ v.decorator.add_link(v, link, name, comment)
+ end
+end
+
+redef class UnknownCommand
+ redef fun render(v, token, model) do
+ var link = token.link
+ if link == null then
+ write_error(v, "Empty command")
+ return
+ end
+ var full_name = link.write_to_string
+ var mentity = find_mentity(model, full_name)
+ if mentity == null then
+ write_error(v, "Unknown command `{link}`")
+ return
+ end
+ write_mentity_link(v, mentity)
+ end
+end
+
+redef class ArticleCommand
+ redef fun render(v, token, model) do
+ if args.is_empty then
+ write_error(v, "Expected one arg: the MEntity name")
+ return
+ end
+ var name = args.first
+ var mentity = find_mentity(model, name)
+ if mentity == null then
+ write_error(v, "No MEntity found for name `{name}`")
+ return
+ end
+ var mdoc = mentity.mdoc_or_fallback
+ if mdoc == null then
+ write_warning(v, "No MDoc for mentity `{name}`")
+ return
+ end
+ v.add "<h3>"
+ write_mentity_link(v, mentity)
+ v.add " - "
+ v.emit_text mdoc.synopsis
+ v.add "</h3>"
+ v.add v.processor.process(mdoc.comment).write_to_string
+ end
+end
+
+redef class CommentCommand
+ redef fun render(v, token, model) do
+ if args.is_empty then
+ write_error(v, "Expected one arg: the MEntity name")
+ return
+ end
+ var name = args.first
+ var mentity = find_mentity(model, name)
+ if mentity == null then
+ write_error(v, "No MEntity found for name `{name}`")
+ return
+ end
+ var mdoc = mentity.mdoc_or_fallback
+ if mdoc == null then
+ write_warning(v, "No MDoc for mentity `{name}`")
+ return
+ end
+ v.add v.processor.process(mdoc.comment).write_to_string
+ end
+end
+
+redef class ListCommand
+ redef fun render(v, token, model) do
+ if args.is_empty then
+ write_error(v, "Expected one arg: the MEntity name")
+ return
+ end
+ var name = args.first
+ var mentity = find_mentity(model, name)
+ if mentity isa MPackage then
+ write_list(v, mentity.mgroups)
+ else if mentity isa MGroup then
+ var res = new Array[MEntity]
+ res.add_all mentity.in_nesting.smallers
+ res.add_all mentity.mmodules
+ write_list(v, res)
+ else if mentity isa MModule then
+ write_list(v, mentity.mclassdefs)
+ else if mentity isa MClass then
+ write_list(v, mentity.collect_intro_mproperties(model))
+ else if mentity isa MClassDef then
+ write_list(v, mentity.mpropdefs)
+ else if mentity isa MProperty then
+ write_list(v, mentity.mpropdefs)
+ else
+ write_error(v, "No list found for name `{name}`")
+ end
+ end
+
+ # Write a mentity list in the output
+ fun write_list(v: MarkdownEmitter, mentities: Collection[MEntity]) do
+ v.add "<ul>"
+ for mentity in mentities do
+ var mdoc = mentity.mdoc_or_fallback
+ v.add "<li>"
+ write_mentity_link(v, mentity)
+ if mdoc != null then
+ v.add " - "
+ v.emit_text mdoc.synopsis
+ end
+ v.add "</li>"
+ end
+ v.add "</ul>"
+ end
+end
+
+redef class CodeCommand
+ redef fun render(v, token, model) do
+ if args.is_empty then
+ write_error(v, "Expected one arg: the MEntity name")
+ return
+ end
+ var name = args.first
+ var mentity = find_mentity(model, name)
+ if mentity == null then
+ write_error(v, "No MEntity found for name `{name}`")
+ return
+ end
+ if mentity isa MClass then mentity = mentity.intro
+ if mentity isa MProperty then mentity = mentity.intro
+ var source = render_source(mentity, v.decorator.as(NitwebDecorator).modelbuilder)
+ if source == null then
+ write_error(v, "No source for MEntity `{name}`")
+ return
+ end
+ v.add "<pre>"
+ v.add source
+ v.add "</pre>"
+ end
+
+ # Highlight `mentity` source code.
+ private fun render_source(mentity: MEntity, modelbuilder: ModelBuilder): nullable HTMLTag do
+ var node = modelbuilder.mentity2node(mentity)
+ if node == null then return null
+ var hl = new HighlightVisitor
+ hl.enter_visit node
+ return hl.html
+ end
+end
+
+redef class GraphCommand
+ redef fun render(v, token, model) do
+ if args.is_empty then
+ write_error(v, "Expected one arg: the MEntity name")
+ return
+ end
+ var name = args.first
+ var mentity = find_mentity(model, name)
+ if mentity == null then
+ write_error(v, "No MEntity found for name `{name}`")
+ return
+ end
+ var g = new InheritanceGraph(mentity, model)
+ v.add g.draw(3, 3).to_svg
+ end
+end
import model_api
import api_catalog
import api_graph
+import api_docdown