Merge: contrib/re_parser
authorJean Privat <jean@pryen.org>
Mon, 27 Feb 2017 18:06:48 +0000 (13:06 -0500)
committerJean Privat <jean@pryen.org>
Mon, 27 Feb 2017 18:06:48 +0000 (13:06 -0500)
# RE Parser

RE parser provides a simple API to regular expression parsing.
It is also able to convert a regular expression into a NFA or a DFA and produce dot files from it.

## Building RE parser

From the `re_parser` directory:

~~~bash
make all
~~~

## RE parser in command line

RE parser can be used as a command line tool to generate NFA and DFA dot files from a regular expression:

~~~bash
./re_parser "a(b|c)+d*"
~~~

Will produce the two files `nfa.dot` and `dfa.dot`.

These can be directly viwed with `xdot`:

~~~bash
xdot nfa.dot
xdot dfa.dot
~~~

Or translated to png images with `dot`:

~~~bash
dot -Tpng -onfa.png nfa.dot
dot -Tpng -odfa.png dfa.dot
~~~

See `man dot` for available formats.

## RE parser as a web app

RE parser comes with a web app that allow users to submit regular expression and see the resulting NFA and DFA.

To run the web app server:

~~~bash
./re_app --host localhost --port 3000
~~~

The server will be available at <a href='http://localhost:3000'>http://localhost:3000</a>.

## RE parser as a library

You can also use RE parser as a library by importing `re_parser`.

~~~nit
import re_parser

var re = "a(b|c)+d*"

# Parse the expression
var l = new Lexer_re_parser(re)
var p = new Parser_re_parser
p.tokens.add_all l.lex
var node = p.parse

# Check errors
if not node isa NProd then
print "Error parsing the regular expression"
abort
end

# Get NFA and DFA
var v = new REVisitor
v.start(node)
print v.nfa.to_dot
print v.nfa.to_dfa.to_dot
~~~

Web app demo: http://re.moz-code.org/

Pull-Request: #2373
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Jean-Christophe Beaupré <jcbrinfo.public@gmail.com>

28 files changed:
contrib/inkscape_tools/src/svg_to_png_and_nit.nit
contrib/nitcc/src/autom.nit
contrib/nitcc/src/grammar.nit
contrib/nitcc/src/nitcc.nit
contrib/pep8analysis/src/parser/lexer.nit
contrib/pep8analysis/src/parser/parser.nit
contrib/pep8analysis/src/parser/parser_abs.nit
lib/core/file.nit
lib/core/stream.nit
lib/json/json_lexer.nit
lib/json/json_parser.nit
lib/json/serialization_write.nit
lib/nitcorn/media_types.nit
lib/popcorn/pop_handlers.nit
src/compiler/abstract_compiler.nit
src/frontend/check_annotation.nit
src/loader.nit
src/model/mmodule.nit
src/nitrestful.nit
src/nitserial.nit
src/parser/lexer.nit
src/parser/parser.nit
src/parser/parser_abs.nit
src/parser/parser_prod.nit
src/parser/xss/main.xss
src/toolcontext.nit
tests/sav/nitrestful_args1.res
tests/sav/nitserial_args1.res

index 982d7f4..fafdc43 100644 (file)
@@ -255,7 +255,7 @@ end
 
 var opt_out_src = new OptionString("Path to output source file (folder or file)", "--src", "-s")
 var opt_assets = new OptionString("Path to assert dir where to put PNG files", "--assets", "-a")
-var opt_scale = new OptionFloat("Apply scaling to exported images (default at 1.0 of 90dpi)", 1.0, "--scale", "-x")
+var opt_scale = new OptionFloat("Apply scaling to exported images (default at 1.0 of 96dpi)", 1.0, "--scale", "-x")
 var opt_gamnit = new OptionBool("Target the Gamnit framework (by default it targets Mnit)", "--gamnit", "-g")
 var opt_pow2 = new OptionBool("Round the image size to the next power of 2", "--pow2")
 var opt_help = new OptionBool("Print this help message", "--help", "-h")
@@ -414,7 +414,7 @@ for drawing in drawings do
        # Output png file to assets
        var png_path = "{assets_path}/images/{drawing_name}.png"
        var proc2 = new Process.from_a(prog, [drawing, "--without-gui",
-               "--export-dpi={(90.0*scale).to_i}",
+               "--export-dpi={(96.0*scale).to_i}",
                "--export-png={png_path}",
                "--export-area={min_x}:{y0}:{max_x}:{y1}",
                "--export-background=#000000", "--export-background-opacity=0.0"])
index 65ddf32..3248e17 100644 (file)
@@ -701,7 +701,7 @@ private class DFAGenerator
                end
 
                add "# Lexer generated by nitcc for the grammar {name}\n"
-               add "module {name}_lexer is no_warning \"missing-doc\"\n"
+               add "module {name}_lexer is generated, no_warning \"missing-doc\"\n"
                add("import nitcc_runtime\n")
 
                var p = parser
index 3dbd8ce..27a98da 100644 (file)
@@ -640,7 +640,7 @@ private class Generator
                var gram = autom.grammar
 
                add "# Parser generated by nitcc for the grammar {name}"
-               add "module {name}_parser is no_warning(\"missing-doc\",\"old-init\")"
+               add "module {name}_parser is generated, no_warning(\"missing-doc\",\"old-init\")"
                add "import nitcc_runtime"
 
                add "class Parser_{name}"
index 3fc823e..3456690 100644 (file)
@@ -146,7 +146,7 @@ f = new FileWriter.open("{name}_test_parser.nit")
 f.write """# Generated by nitcc for the language {{{name}}}
 
 # Standalone parser tester for the language {{{name}}}
-module {{{name}}}_test_parser
+module {{{name}}}_test_parser is generated
 import nitcc_runtime
 import {{{name}}}_lexer
 import {{{name}}}_parser
index 718ee98..c663dcd 100644 (file)
@@ -1,6 +1,6 @@
 # Lexer and its tokens.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module lexer is no_warning("missing-doc", "old-init")
+module lexer is generated, no_warning("missing-doc", "old-init")
 
 intrude import parser_nodes
 private import tables
index 3fd9171..9f1ae4d 100644 (file)
@@ -1,6 +1,6 @@
 # Parser.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module parser is no_warning("missing-doc", "old-init")
+module parser is generated, no_warning("missing-doc", "old-init")
 
 intrude import parser_prod
 import tables
index a0c8af4..7518f84 100644 (file)
@@ -1,6 +1,6 @@
 # Raw AST node hierarchy.
 # This file was generated by SableCC (http://www.sablecc.org/).
-package parser_abs
+package parser_abs is generated
 
 import location
 
index c78f6d4..baa3403 100644 (file)
@@ -1057,7 +1057,8 @@ redef class String
        # ~~~
        fun simplify_path: String
        do
-               var a = self.split_with("/")
+               var path_sep = if is_windows then "\\" else "/"
+               var a = self.split_with(path_sep)
                var a2 = new Array[String]
                for x in a do
                        if x == "." and not a2.is_empty then continue # skip `././`
index 217e9c8..6fbc63c 100644 (file)
@@ -668,18 +668,24 @@ end
 class StringWriter
        super Writer
 
-       private var content = new Array[String]
-       redef fun to_s do return content.plain_to_s
+       private var content = new Buffer
+       redef fun to_s do return content.to_s
        redef fun is_writable do return not closed
 
        redef fun write_bytes(b) do
-               content.add(b.to_s)
+               content.append(b.to_s)
        end
 
        redef fun write(str)
        do
                assert not closed
-               content.add(str.to_s)
+               content.append(str)
+       end
+
+       redef fun write_char(c)
+       do
+               assert not closed
+               content.add(c)
        end
 
        # Is the stream closed?
index 8ec28ff..32a82b2 100644 (file)
@@ -1,5 +1,5 @@
 # Lexer generated by nitcc for the grammar json
-module json_lexer is no_warning "missing-doc"
+module json_lexer is generated, no_warning "missing-doc"
 import nitcc_runtime
 import json_parser
 class Lexer_json
index 7dc04ce..28094c0 100644 (file)
@@ -1,5 +1,5 @@
 # Parser generated by nitcc for the grammar json
-module json_parser is no_warning("missing-doc","old-init")
+module json_parser is generated, no_warning("missing-doc","old-init")
 import nitcc_runtime
 class Parser_json
        super Parser
index 43987e8..28e79a8 100644 (file)
@@ -144,26 +144,49 @@ redef class Text
        redef fun accept_json_serializer(v)
        do
                v.stream.write "\""
+
+               var start_i = 0
+               var escaped = null
                for i in [0 .. self.length[ do
                        var char = self[i]
                        if char == '\\' then
-                               v.stream.write "\\\\"
+                               escaped = "\\\\"
                        else if char == '\"' then
-                               v.stream.write "\\\""
+                               escaped = "\\\""
                        else if char < ' ' then
                                if char == '\n' then
-                                       v.stream.write "\\n"
+                                       escaped = "\\n"
                                else if char == '\r' then
-                                       v.stream.write "\\r"
+                                       escaped = "\\r"
                                else if char == '\t' then
-                                       v.stream.write "\\t"
+                                       escaped = "\\t"
                                else
-                                       v.stream.write char.escape_to_utf16
+                                       escaped = char.escape_to_utf16
                                end
+                       end
+
+                       if escaped != null then
+                               # Write open non-escaped string
+                               if start_i <= i then
+                                       v.stream.write substring(start_i, i-start_i)
+                               end
+
+                               # Write escaped character
+                               v.stream.write escaped
+                               escaped = null
+                               start_i = i+1
+                       end
+               end
+
+               # Write remaining non-escaped string
+               if start_i < length then
+                       if start_i == 0 then
+                               v.stream.write self
                        else
-                               v.stream.write char.to_s
+                               v.stream.write substring(start_i, length-start_i)
                        end
                end
+
                v.stream.write "\""
        end
 end
index 305f314..49b6daa 100644 (file)
@@ -37,6 +37,7 @@ class MediaTypes
                types["htm"]        = "text/html"
                types["shtml"]      = "text/html"
                types["css"]        = "text/css"
+               types["csv"]        = "text/csv"
                types["xml"]        = "text/xml"
                types["rss"]        = "text/xml"
                types["gif"]        = "image/gif"
@@ -50,6 +51,7 @@ class MediaTypes
                types["ico"]        = "image/x-icon"
                types["jng"]        = "image/x-jng"
                types["wbmp"]       = "image/vnd.wap.wbmp"
+               types["gz"]         = "application/gzip"
                types["jar"]        = "application/java-archive"
                types["war"]        = "application/java-archive"
                types["ear"]        = "application/java-archive"
index 8d66cc9..f55126f 100644 (file)
@@ -20,6 +20,7 @@ module pop_handlers
 import pop_routes
 import json::static
 import json
+import csv
 
 # Class handler for a route.
 #
@@ -459,6 +460,16 @@ redef class HttpResponse
                end
        end
 
+       # Write data as CSV and set the right content type header.
+       fun csv(csv: nullable CsvDocument, status: nullable Int) do
+               header["Content-Type"] = media_types["csv"].as(not null)
+               if csv == null then
+                       send(null, status)
+               else
+                       send(csv.write_to_string, status)
+               end
+       end
+
        # Write error as JSON and set the right content type header.
        fun json_error(error: nullable Jsonable, status: nullable Int) do
                json(error, status)
index d608967..5a0b684 100644 (file)
@@ -403,6 +403,18 @@ ifeq ($(uname_S),Darwin)
        LDLIBS := $(filter-out -lrt,$(LDLIBS))
 endif
 
+# Special configuration for Windows under mingw64
+ifeq ($(uname_S),MINGW64_NT-10.0)
+       # Use the pcreposix regex library
+       LDLIBS += -lpcreposix
+
+       # Remove POSIX flag -lrt
+       LDLIBS := $(filter-out -lrt,$(LDLIBS))
+
+       # Silence warnings when storing Int, Char and Bool as pointer address
+       CFLAGS += -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast
+endif
+
 """
 
                makefile.write("all: {outpath}\n")
@@ -510,8 +522,10 @@ endif
                self.toolcontext.info(command, 2)
 
                var res
-               if self.toolcontext.verbose_level >= 3 or is_windows then
+               if self.toolcontext.verbose_level >= 3 then
                        res = sys.system("{command} 2>&1")
+               else if is_windows then
+                       res = sys.system("{command} 2>&1 >nul")
                else
                        res = sys.system("{command} 2>&1 >/dev/null")
                end
index 680fa67..3dd8042 100644 (file)
@@ -94,6 +94,7 @@ abstract
 intern
 extern
 no_warning
+generated
 
 auto_inspect
 
index 32749aa..37812f7 100644 (file)
@@ -813,6 +813,8 @@ redef class ModelBuilder
                        end
                        # Is the module a test suite?
                        mmodule.is_test_suite = not decl.get_annotations("test_suite").is_empty
+                       # Is the module generated?
+                       mmodule.is_generated = not decl.get_annotations("generated").is_empty
                end
        end
 
index 44eaffb..9d1fb9c 100644 (file)
@@ -251,6 +251,12 @@ class MModule
        # Is `self` a unit test module used by `nitunit`?
        var is_test_suite: Bool = false is writable
 
+       # Is `self` a module generated by a tool?
+       #
+       # This flag has no effect on the semantic.
+       # It is only intended on software engineering software to discriminate computer-generated modules from human-written ones.
+       var is_generated: Bool = false is writable
+
        # Get the non-`is_fictive` module on which `self` is based on.
        #
        # On non-fictive module, this returns `self`.
index 2303974..6a93f30 100644 (file)
@@ -193,6 +193,7 @@ else
 end
 
 var nit_module = new NitModule(module_name)
+nit_module.annotations.add """generated"""
 nit_module.annotations.add """no_warning("parentheses")"""
 nit_module.header = """
 # This file is generated by nitrestful
index f7481d4..574bc5d 100644 (file)
@@ -158,6 +158,7 @@ for mmodule in mmodules do
        if importations == null then importations = target_modules
 
        var nit_module = new NitModule(module_name)
+       nit_module.annotations.add """generated"""
        nit_module.annotations.add """no_warning("property-conflict")"""
        nit_module.header = """
 # This file is generated by nitserial
index 0bf30f2..f5cf331 100644 (file)
@@ -1,6 +1,6 @@
 # Lexer and its tokens.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module lexer is no_warning("missing-doc")
+module lexer is generated, no_warning("missing-doc")
 
 intrude import parser_nodes
 intrude import lexer_work
index a58664d..8227bef 100644 (file)
@@ -1,6 +1,6 @@
 # Parser.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module parser is no_warning("missing-doc", "unread-variable")
+module parser is generated, no_warning("missing-doc", "unread-variable")
 
 intrude import parser_prod
 intrude import parser_work
index bbf34da..fc6dba5 100644 (file)
@@ -1,6 +1,6 @@
 # Raw AST node hierarchy.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module parser_abs is no_warning("missing-doc")
+module parser_abs is generated, no_warning("missing-doc")
 
 import location
 
index 0e5b1e3..08e7292 100644 (file)
@@ -1,6 +1,6 @@
 # Production AST nodes full definition.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module parser_prod is no_warning("missing-doc")
+module parser_prod is generated, no_warning("missing-doc")
 
 import lexer
 intrude import parser_nodes
index fae2ea4..50ff14c 100644 (file)
@@ -23,7 +23,7 @@ $ include 'prods.xss'
 $ output 'parser_abs.nit'
 # Raw AST node hierarchy.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module parser_abs is no_warning("missing-doc")
+module parser_abs is generated, no_warning("missing-doc")
 
 import location
 
@@ -34,7 +34,7 @@ $ end output
 $ output 'lexer.nit'
 # Lexer and its tokens.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module lexer is no_warning("missing-doc")
+module lexer is generated, no_warning("missing-doc")
 
 $ if $usermodule
 intrude import $usermodule
@@ -51,7 +51,7 @@ $ end output
 $ output 'parser_prod.nit'
 # Production AST nodes full definition.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module parser_prod is no_warning("missing-doc")
+module parser_prod is generated, no_warning("missing-doc")
 
 import lexer
 $ if $usermodule
@@ -67,7 +67,7 @@ $ end output
 $ output 'parser.nit'
 # Parser.
 # This file was generated by SableCC (http://www.sablecc.org/).
-module parser is no_warning("missing-doc", "unread-variable")
+module parser is generated, no_warning("missing-doc", "unread-variable")
 
 intrude import parser_prod
 intrude import parser_work
index bc99f81..4fd3fa6 100644 (file)
@@ -566,7 +566,7 @@ The Nit language documentation and the source code of its tools and libraries ma
        #
        # It uses, in order:
        #
-       # * the option `opt_no_color`
+       # * the option `opt_nit_dir`
        # * the environment variable `NIT_DIR`
        # * the runpath of the program from argv[0]
        # * the runpath of the process from /proc
@@ -610,7 +610,8 @@ The Nit language documentation and the source code of its tools and libraries ma
                end
 
                # search in the PATH
-               var ps = "PATH".environ.split(":")
+               var path_sep = if is_windows then ";" else ":"
+               var ps = "PATH".environ.split(path_sep)
                for p in ps do
                        res = p/".."
                        if check_nit_dir(res) then return res.simplify_path
index ea30917..21b65cb 100644 (file)
@@ -1,6 +1,7 @@
 # This file is generated by nitrestful
 # Do not modify, instead refine the generated services.
 module restful_annot_rest is
+       generated
        no_warning("parentheses")
 end
 
index f82f50e..4c6679d 100644 (file)
@@ -1,6 +1,7 @@
 # This file is generated by nitserial
 # Do not modify, but you can redef
 module test_serialization_serial is
+       generated
        no_warning("property-conflict")
 end