1
0
mirror of https://github.com/pdemian/human2regex.git synced 2025-05-16 12:30:09 -07:00

Added codemirror editor

This commit is contained in:
Patrick Demian 2020-11-01 06:53:07 -05:00
parent a23efd3904
commit 92fc7445d5
9 changed files with 149 additions and 66 deletions

5
docs/bundle.min.css vendored

File diff suppressed because one or more lines are too long

18
docs/bundle.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
<!DOCTYPE html><html lang="en" dir="ltr"><head><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content="Create regular expressions with natural, human language"><meta name="keywords" content="Human2Regex, Human, Regex, Natural, Language, Natural Language"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Human2Regex</title><link href="bundle.min.css" rel="stylesheet" type="text/css"><meta name="theme-color" content="#212529"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><link rel="icon" type="image/x-icon" href="favicon.ico"></head><body><a class="skip skip-top" href="#maincontent">Skip to main content</a><div class="wrapper"><nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav"><div class="container"><a class="navbar-brand" href="index.html"><img src="favicon.png" width="30" height="30" class="d-inline-block align-top" alt="logo">&nbsp;Human2Regex</a></div></nav><div class="container" id="maincontent" role="main"><div class="row"><div class="col-lg-8"><div class="form-group row zero-margin-bottom"><p class="col-sm-4">Regex dialect:</p><span class="col-sm-8"><select class="form-control" id="dialect"><option value="js" selected="selected">Javascript</option><option value="dotnet">.NET</option><option value="java">Java</option><option value="perl">Perl</option></select></span></div><h4>Your Regular Expression:</h4><input readonly="readonly" class="form-control" id="regex"><h4>Human Speak:</h4><textarea class="form-control" id="human" rows="25" onkeydown='if(9===event.keyCode){var v=this.value,s=this.selectionStart,e=this.selectionEnd;return this.value=v.substring(0,s)+"\t"+v.substring(e),this.selectionStart=this.selectionEnd=s+1,!1}'> <!DOCTYPE html><html lang="en" dir="ltr"><head><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="description" content="Create regular expressions with natural, human language"><meta name="keywords" content="Human2Regex, Human, Regex, Natural, Language, Natural Language"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Human2Regex</title><link href="bundle.min.css" rel="stylesheet" type="text/css"><meta name="theme-color" content="#212529"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><link rel="icon" type="image/x-icon" href="favicon.ico"></head><body><a class="skip skip-top" href="#maincontent">Skip to main content</a><div class="wrapper"><nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav"><div class="container"><a class="navbar-brand" href="index.html"><img src="favicon.png" width="30" height="30" class="d-inline-block align-top" alt="logo">&nbsp;Human2Regex</a></div></nav><div class="container" id="maincontent" role="main"><div class="row"><div class="col-lg-8 tenpx-margin-bottom"><div class="form-group row zero-margin-bottom"><label for="dialect" class="col-sm-4 col-form-label">Regex dialect:</label><div class="col-sm-8"><select class="form-control" id="dialect"><option value="js" selected="selected">Javascript</option><option value="dotnet">.NET</option><option value="java">Java</option><option value="perl">Perl</option></select></div></div><h4>Your Regular Expression:</h4><div class="row"><div class="col-xl-11"><input readonly="readonly" class="form-control" id="regex"></div><div class="col-xl-1"><button type="button" class="btn btn-secondary" id="clip">Copy</button></div></div><h4>Human Speak:</h4><textarea class="form-control" id="human" rows="25">
// H2R supports // # and /**/ as comments // H2R supports // # and /**/ as comments
// A group is only captured if given a name. // A group is only captured if given a name.
// You can use "and", "or", "not" to specify "[]" regex // You can use "and", "or", "not" to specify "[]" regex
@ -37,4 +37,4 @@ create an optional group
# fragment, again, we don't care, so ignore everything afterwards # fragment, again, we don't care, so ignore everything afterwards
match "#" match "#"
match 0+ any thing match 0+ any thing
</textarea></div><br><div class="col-lg-4 margin-when-small"><div class="cheatsheet"><h2>Cheat Sheet:</h2><p>Full documentation available <a href="tutorial.html">here</a></p><p class="font-weight-bold">Matching</p><p><code>match "hello world"</code> matches "hello world" exactly</p><p></p><p><code>match "hello" then optionally " world"</code> matches "hello" or "hello world"</p><p><code>match "hello" or "world"</code> matches "hello" or "world</p><p><code>match a word</code> matches any word</p><p class="font-weight-bold">Repetition</p><p><code>match 0+ "hello"</code> matches 0 or more "hello"s</p><p><code>match 3 "hello"</code> matches exactly "hellohellohello"</p><p><code>match 1 to 5 "hello"</code> matches between 1 to 5 "hello"s</p><p><code>repeat 0 or more</code> repeats the intended text 0 or more times (default)</p><p><code>optionally repeat between 3 to 5</code> optionally repeats the indented text 3 to 5 times</p><p class="font-weight-bold">Grouping</p><p><code>create a group called "mygroup"</code> creates a group called "mygroup"</p><p><code>create an optional group</code> creates an unnamed optional group</p></div></div></div></div><footer><div class="container"><div class="row"><div class="col-lg-8 col-md-10 mx-auto"><p class="copyright">Copyright &copy; 2020 Patrick Demian. This page's source code is available at <a rel="noopener noreferrer" href="https://github.com/pdemian/">github.com/pdemian/</a></p></div></div></div></footer></div><script defer="defer" src="bundle.min.js"></script></body></html> </textarea><h4>Errors:</h4><textarea readonly="readonly" class="form-control" id="errors" rows="5"></textarea></div><br><div class="col-lg-4 tenpx-margin-bottom"><div class="cheatsheet"><h2>Cheat Sheet:</h2><p>Full documentation available <a href="tutorial.html">here</a></p><p class="font-weight-bold">Matching</p><p><code>match "hello world"</code> matches "hello world" exactly</p><p></p><p><code>match "hello" then optionally " world"</code> matches "hello" or "hello world"</p><p><code>match "hello" or "world"</code> matches "hello" or "world</p><p><code>match a word</code> matches any word</p><p class="font-weight-bold">Repetition</p><p><code>match 0+ "hello"</code> matches 0 or more "hello"s</p><p><code>match 3 "hello"</code> matches exactly "hellohellohello"</p><p><code>match 1 to 5 "hello"</code> matches between 1 to 5 "hello"s</p><p><code>repeat 0 or more</code> repeats the intended text 0 or more times (default)</p><p><code>optionally repeat between 3 to 5</code> optionally repeats the indented text 3 to 5 times</p><p class="font-weight-bold">Grouping</p><p><code>create a group called "mygroup"</code> creates a group called "mygroup"</p><p><code>create an optional group</code> creates an unnamed optional group</p></div></div></div></div><footer><div class="container"><div class="row"><div class="col-lg-8 col-md-10 mx-auto"><p class="copyright">Copyright &copy; 2020 Patrick Demian. This page's source code is available at <a rel="noopener noreferrer" href="https://github.com/pdemian/">github.com/pdemian/</a></p></div></div></div></footer></div><script defer="defer" src="bundle.min.js"></script></body></html>

5
package-lock.json generated
View File

@ -2400,6 +2400,11 @@
"q": "^1.1.2" "q": "^1.1.2"
} }
}, },
"codemirror": {
"version": "5.58.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.58.2.tgz",
"integrity": "sha512-K/hOh24cCwRutd1Mk3uLtjWzNISOkm4fvXiMO7LucCrqbh6aJDdtqUziim3MZUI6wOY0rvY1SlL1Ork01uMy6w=="
},
"collect-v8-coverage": { "collect-v8-coverage": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",

View File

@ -39,6 +39,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"chevrotain": "^7.0.2", "chevrotain": "^7.0.2",
"codemirror": "^5.58.2",
"jquery": "^3.5.1" "jquery": "^3.5.1"
}, },
"repository": { "repository": {

View File

@ -4,29 +4,33 @@
import { Human2RegexLexer, Human2RegexLexerOptions } from "./lexer"; import { Human2RegexLexer, Human2RegexLexerOptions } from "./lexer";
import { Human2RegexParser, Human2RegexParserOptions } from "./parser"; import { Human2RegexParser, Human2RegexParserOptions } from "./parser";
import { RobotLanguage } from "./generator"; import { RobotLanguage } from "./generator";
import { lexErrorToCommonError, parseErrorToCommonError, semanticErrorToCommonError, ICommonError } from "./utilities"; import { CommonError } from "./utilities";
import $ from "jquery"; import $ from "jquery";
import CodeMirror from "codemirror/lib/codemirror";
require("codemirror/mode/javascript/javascript");
import "./webpage/bootstrap.css"; import "./webpage/bootstrap.css";
import "./webpage/cleanblog.css"; import "./webpage/cleanblog.css";
import "codemirror/lib/codemirror.css";
import "./webpage/style.css"; import "./webpage/style.css";
$(function() { $(function() {
const total_errors: ICommonError[] = []; const total_errors: CommonError[] = [];
const lexer = new Human2RegexLexer(new Human2RegexLexerOptions(true)); const lexer = new Human2RegexLexer(new Human2RegexLexerOptions(true));
const parser = new Human2RegexParser(new Human2RegexParserOptions(true)); const parser = new Human2RegexParser(new Human2RegexParserOptions(true));
const result = lexer.tokenize($("#human").text()); const result = lexer.tokenize($("#human").text());
result.errors.map(lexErrorToCommonError).forEach((x) => total_errors.push(x)); result.errors.map(CommonError.fromLexError).forEach((x) => total_errors.push(x));
let regex_result = "";
if (total_errors.length === 0) { if (total_errors.length === 0) {
parser.input = result.tokens; parser.input = result.tokens;
const regex = parser.parse(); const regex = parser.parse();
parser.errors.map(parseErrorToCommonError).forEach((x) => total_errors.push(x)); parser.errors.map(CommonError.fromParseError).forEach((x) => total_errors.push(x));
let lang: RobotLanguage = RobotLanguage.JS; let lang: RobotLanguage = RobotLanguage.JS;
switch ($("#dialect option:selected").val()) { switch ($("#dialect option:selected").val()) {
@ -46,15 +50,21 @@ $(function() {
const valid = regex.validate(lang); const valid = regex.validate(lang);
valid.map(semanticErrorToCommonError).forEach((x) => total_errors.push(x)); valid.map(CommonError.fromSemanticError).forEach((x) => total_errors.push(x));
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (total_errors.length === 0) { if (total_errors.length === 0) {
const r = regex.toRegex(lang); regex_result = regex.toRegex(lang);
$("#regex").attr("value", r); $("#regex").attr("value", regex_result);
} }
} }
$("#errors").empty();
for (const error of total_errors) {
$("#errors").append(`${error.toString()}\n`);
}
console.log("Errors = " + total_errors); console.log("Errors = " + total_errors);
$("#dialect").on("selectionchanged", () => { $("#dialect").on("selectionchanged", () => {
@ -64,6 +74,31 @@ $(function() {
$("#human").on("input", () => { $("#human").on("input", () => {
//do something //do something
}); });
$("#clip").on("click", () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (window.isSecureContext && navigator?.clipboard?.writeText) {
navigator.clipboard.writeText(regex_result);
}
else {
const text = document.getElementById("regex") as any;
text.select();
text.setSelectionRange(0, 10*10*10*10);
document.execCommand("copy");
}
});
const editor = CodeMirror.fromTextArea(document.getElementById("human"), {
mode: {name: "javascript"},
lineNumbers: false,
indentUnit: 4,
viewportMargin: Infinity
});
editor.on("change", (instance: unknown, change_obj: unknown) => {
/* not empty */
console.log(editor.getValue());
});
}); });

View File

@ -54,35 +54,24 @@ export function regexEscape(input: string) : string {
return input.replace("\\", "\\\\").replace(/([=:\-\.\[\]\^\|\(\)\*\+\?\{\}\$\/])/g, "\\$1"); return input.replace("\\", "\\\\").replace(/([=:\-\.\[\]\^\|\(\)\*\+\?\{\}\$\/])/g, "\\$1");
} }
export interface ICommonError { export class CommonError {
type: string, constructor(public type: string, public start_line: number, public start_column: number, public length: number, public message: string) {
startLine: number, /* empty */
startColumn: number, }
length: number,
message: string
}
export function lexErrorToCommonError(error: ILexingError): ICommonError { public static fromLexError(error: ILexingError): CommonError {
return { return new CommonError("Lexer Error", error.line, error.column, error.length, error.message);
type: "Lexer Error", }
startLine: error.line,
startColumn: error.column,
length: error.length,
message: error.message
};
}
export function parseErrorToCommonError(error: IRecognitionException): ICommonError { public static fromParseError(error: IRecognitionException): CommonError {
return { return new CommonError("Parser Error", error.token.startLine ?? NaN, error.token.startColumn ?? NaN, error.token.endOffset ?? NaN - error.token.startOffset, error.name + ": " + error.message);
type: "Parser Error", }
startLine: error.token.startLine ?? NaN,
startColumn: error.token.startColumn ?? NaN,
length: error.token.endOffset ?? NaN - error.token.startOffset,
message: error.name + ": " + error.message,
};
}
export function semanticErrorToCommonError(error: ISemanticError): ICommonError { public static fromSemanticError(error: ISemanticError): CommonError {
(error as ICommonError).type = "Semantic Error"; return new CommonError("Semantic Error", error.startLine, error.startColumn, error.length, error.message);
return error as ICommonError; }
public toString(): string {
return `${this.type} @ ${this.start_line} ${this.start_column}: ${this.message}`;
}
} }

View File

@ -2,21 +2,30 @@
<!-- Main Content --> <!-- Main Content -->
<div class="container" id="maincontent" role="main"> <div class="container" id="maincontent" role="main">
<div class="row"> <div class="row">
<div class="col-lg-8"> <div class="col-lg-8 tenpx-margin-bottom">
<div class="form-group row zero-margin-bottom"> <div class="form-group row zero-margin-bottom">
<p class="col-sm-4">Regex dialect:</p> <label for="dialect" class="col-sm-4 col-form-label">Regex dialect:</label>
<span class="col-sm-8"> <div class="col-sm-8">
<select class="form-control" id="dialect"> <select class="form-control" id="dialect">
<option value="js" selected>Javascript</option> <option value="js" selected>Javascript</option>
<option value="dotnet">.NET</option> <option value="dotnet">.NET</option>
<option value="java">Java</option> <option value="java">Java</option>
<option value="perl">Perl</option> <option value="perl">Perl</option>
</select> </select>
</div>
</div> </div>
<h4>Your Regular Expression:</h4> <h4>Your Regular Expression:</h4>
<input readonly type="text" class="form-control" id="regex"></input> <div class="row">
<div class="col-xl-11">
<input readonly type="text" class="form-control" id="regex"></input>
</div>
<div class="col-xl-1">
<button type="button" class="btn btn-secondary" id="clip">Copy</button>
</div>
</div>
<h4>Human Speak:</h4> <h4>Human Speak:</h4>
<textarea class="form-control" id="human" rows="25" onkeydown="if(event.keyCode===9){var v=this.value,s=this.selectionStart,e=this.selectionEnd;this.value=v.substring(0, s)+'\t'+v.substring(e);this.selectionStart=this.selectionEnd=s+1;return false;}"> <textarea class="form-control" id="human" rows="25">
// H2R supports // # and /**/ as comments // H2R supports // # and /**/ as comments
// A group is only captured if given a name. // A group is only captured if given a name.
// You can use "and", "or", "not" to specify "[]" regex // You can use "and", "or", "not" to specify "[]" regex
@ -56,9 +65,11 @@ create an optional group
match "#" match "#"
match 0+ any thing match 0+ any thing
</textarea> </textarea>
<h4>Errors:</h4>
<textarea readonly class="form-control " id="errors" rows="5"></textarea>
</div> </div>
<br> <br>
<div class="col-lg-4 margin-when-small"> <div class="col-lg-4 tenpx-margin-bottom">
<div class="cheatsheet"> <div class="cheatsheet">
<h2>Cheat Sheet:</h2> <h2>Cheat Sheet:</h2>
<p>Full documentation available <a href="tutorial.html">here</a></p> <p>Full documentation available <a href="tutorial.html">here</a></p>

View File

@ -1,10 +1,7 @@
/*! Copyright (c) 2020 Patrick Demian; Licensed under MIT */ /*! Copyright (c) 2020 Patrick Demian; Licensed under MIT */
@media (max-width: 992px) { .tenpx-margin-bottom {
.margin-when-small { margin-bottom: 10px;
margin-top: 10px;
margin-bottom: 10px;
}
} }
h4 { h4 {
@ -81,4 +78,48 @@ a:hover {
.navbar-light .navbar-nav .nav-link { .navbar-light .navbar-nav .nav-link {
color: rgba(0, 0, 0, 0.54); color: rgba(0, 0, 0, 0.54);
}
.navbar {
padding: 0 !important;
}
#errors {
resize: none;
background-color: #FFF;
}
.CodeMirror {
/* Bootstrap Settings */
box-sizing: border-box;
margin: 0;
font: inherit;
overflow: auto;
font-family: inherit;
display: block;
width: 100%;
padding: 6px 6px;
font-size: 14px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
/* Code Mirror Settings */
font-family: monospace;
position: relative;
overflow: hidden;
resize: vertical;
height: 400px;
}
.CodeMirror-focused {
/* Bootstrap Settings */
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0.2rem rgba(102, 175, 233, .6);
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
} }