From debec10dbade71785abf0fe58906e53656fc7efc Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 4 May 2021 05:56:34 -0400 Subject: [PATCH] fix: make globals static so that they don't pollute downstream libraries (#42) Also reorganized top level `let`s into static variables. --- .prettierignore | 3 +- asconfig.empty.json | 8 ++ assembly/__tests__/empty.ts | 1 + assembly/__tests__/empty.wat | 5 + assembly/char.ts | 2 + assembly/nfa/matcher.ts | 25 ++-- assembly/nfa/nfa.ts | 20 ++- assembly/nfa/types.ts | 10 ++ assembly/parser/node.ts | 13 +- assembly/parser/walker.ts | 11 +- assembly/regexp.ts | 9 +- package-lock.json | 247 +++++++++++++++++++++++++++++++++-- package.json | 5 +- 13 files changed, 306 insertions(+), 53 deletions(-) create mode 100644 asconfig.empty.json create mode 100644 assembly/__tests__/empty.ts create mode 100644 assembly/__tests__/empty.wat create mode 100644 assembly/nfa/types.ts diff --git a/.prettierignore b/.prettierignore index b3decf7..3f09921 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ # prettier doesn't support decorators on functions :-( -assembly/char.ts \ No newline at end of file +assembly/char.ts +assembly/nfa/types.ts \ No newline at end of file diff --git a/asconfig.empty.json b/asconfig.empty.json new file mode 100644 index 0000000..fce89aa --- /dev/null +++ b/asconfig.empty.json @@ -0,0 +1,8 @@ +{ + "options": { + "runtime": "stub", + "textFile": "build/empty.wat", + "debug": true + }, + "entries": ["assembly/__tests__/empty.ts"] +} diff --git a/assembly/__tests__/empty.ts b/assembly/__tests__/empty.ts new file mode 100644 index 0000000..bedc790 --- /dev/null +++ b/assembly/__tests__/empty.ts @@ -0,0 +1 @@ +import * as regex from ".."; diff --git a/assembly/__tests__/empty.wat b/assembly/__tests__/empty.wat new file mode 100644 index 0000000..bffe105 --- /dev/null +++ b/assembly/__tests__/empty.wat @@ -0,0 +1,5 @@ +(module + (memory $0 0) + (table $0 1 funcref) + (export "memory" (memory $0)) +) diff --git a/assembly/char.ts b/assembly/char.ts index 6028514..0e8a6d7 100644 --- a/assembly/char.ts +++ b/assembly/char.ts @@ -1,3 +1,5 @@ +// @ts-ignore +@lazy export const enum Char { None = -1, HorizontalTab = 0x09, diff --git a/assembly/nfa/matcher.ts b/assembly/nfa/matcher.ts index 64b080b..31fe824 100644 --- a/assembly/nfa/matcher.ts +++ b/assembly/nfa/matcher.ts @@ -16,10 +16,9 @@ const enum MatcherType { CharacterSet, CharacterClass, } - -let _flags: Flags; - export class Matcher { + @lazy static _flags: Flags; + constructor(readonly type: MatcherType) {} matches(code: u32): bool { @@ -47,20 +46,23 @@ export class Matcher { node: CharacterClassNode, flags: Flags ): CharacterClassMatcher { - _flags = flags; + Matcher._flags = flags; const matchers = node.expressions.map((exp) => { switch (exp.type) { case NodeType.CharacterRange: return Matcher.fromCharacterRangeNode( exp as CharacterRangeNode, - _flags + Matcher._flags ); case NodeType.Character: - return Matcher.fromCharacterNode(exp as CharacterNode, _flags); + return Matcher.fromCharacterNode( + exp as CharacterNode, + Matcher._flags + ); case NodeType.CharacterSet: return Matcher.fromCharacterClassNode( exp as CharacterSetNode, - _flags + Matcher._flags ); default: throw new Error("unsupported node type within character set"); @@ -93,9 +95,12 @@ export class CharacterMatcher extends Matcher { } } -const LOWERCASE_LETTERS = new Range(Char.a, Char.z); -const UPPERCASE_LETTERS = new Range(Char.A, Char.Z); -const UPPER_LOWER_OFFSET = Char.a - Char.A; +// @ts-ignore +@lazy const LOWERCASE_LETTERS = new Range(Char.a, Char.z); +// @ts-ignore +@lazy const UPPERCASE_LETTERS = new Range(Char.A, Char.Z); +// @ts-ignore +@lazy const UPPER_LOWER_OFFSET = Char.a - Char.A; export class CharacterRangeMatcher extends Matcher { private ranges: Range[]; diff --git a/assembly/nfa/nfa.ts b/assembly/nfa/nfa.ts index 9953a81..5e30b4c 100644 --- a/assembly/nfa/nfa.ts +++ b/assembly/nfa/nfa.ts @@ -14,21 +14,15 @@ import { import { Char } from "../char"; import { Matcher } from "./matcher"; import { Flags } from "../regexp"; - -export enum MatchResult { - // a match has occurred - which is a signal to consume a character - Match, - // a match failed, abort this regex - Fail, - // this state doesn't preform a match - Ignore, -} - -let _stateId: u32 = 0; +import { MatchResult } from "./types"; /* eslint @typescript-eslint/no-empty-function: ["error", { "allow": ["constructors", "methods"] }] */ export class State { - constructor(public transitions: State[] = [], public id: u32 = _stateId++) {} + @lazy static _stateId: u32 = 0; + constructor( + public transitions: State[] = [], + public id: u32 = State._stateId++ + ) {} matches(input: string, position: u32): MatchResult { return MatchResult.Ignore; @@ -253,3 +247,5 @@ class AutomataFactor { } } } + +export { MatchResult } from "./types"; diff --git a/assembly/nfa/types.ts b/assembly/nfa/types.ts new file mode 100644 index 0000000..c1da763 --- /dev/null +++ b/assembly/nfa/types.ts @@ -0,0 +1,10 @@ +// @ts-ignore +@lazy +export enum MatchResult { + // a match has occurred - which is a signal to consume a character + Match, + // a match failed, abort this regex + Fail, + // this state doesn't preform a match + Ignore, +} diff --git a/assembly/parser/node.ts b/assembly/parser/node.ts index 3505379..5d88256 100644 --- a/assembly/parser/node.ts +++ b/assembly/parser/node.ts @@ -15,13 +15,12 @@ export const enum NodeType { Group, } -const emptyNodeArray = new Array(); - export abstract class Node { + @lazy static readonly emptyArray: Node[] = new Array(); constructor(public type: NodeType) {} children(): Node[] { - return emptyNodeArray; + return Node.emptyArray; } abstract clone(): Node; @@ -37,7 +36,7 @@ export class AST extends Node { } children(): Node[] { - return this.body != null ? [this.body as Node] : emptyNodeArray; + return this.body != null ? [this.body as Node] : Node.emptyArray; } clone(): Node { @@ -210,9 +209,9 @@ export class AlternationNode extends Node { } } -let _id = 0; - export class GroupNode extends Node { + @lazy static _id: i32 = 0; + constructor( public expression: Node, public capturing: bool, @@ -220,7 +219,7 @@ export class GroupNode extends Node { ) { super(NodeType.Group); if (id == -1) { - this.id = _id++; + this.id = GroupNode._id++; } } diff --git a/assembly/parser/walker.ts b/assembly/parser/walker.ts index 88ae4ed..097b255 100644 --- a/assembly/parser/walker.ts +++ b/assembly/parser/walker.ts @@ -34,10 +34,13 @@ export function walker(ast: AST, visitor: (node: NodeVisitor) => void): void { } } -// range quantifiers are implemented via 'expansion', which significantly -// increases the size of the AST. This imposes a hard limit to prevent -// memory-related issues -const QUANTIFIER_LIMIT = 1000; +/** + range quantifiers are implemented via 'expansion', which significantly + increases the size of the AST. This imposes a hard limit to prevent + memory-related issues +*/ +// @ts-ignore +@lazy const QUANTIFIER_LIMIT = 1000; function parentAsConcatNode(visitor: NodeVisitor): ConcatenationNode { let concatNode: ConcatenationNode | null = null; diff --git a/assembly/regexp.ts b/assembly/regexp.ts index 8620036..ce353e4 100644 --- a/assembly/regexp.ts +++ b/assembly/regexp.ts @@ -69,8 +69,6 @@ export class Match { } } -let gm = new Array(); - export class Flags { global: bool = false; ignoreCase: bool = false; @@ -112,6 +110,7 @@ function lastCapturesForGroup(groupMarkers: GroupStartMarkerState[]): string[] { } export class RegExp { + @lazy static gm: GroupStartMarkerState[] = new Array(); lastIndex: i32 = 0; private flags: Flags; private nfa: Automata; @@ -136,16 +135,16 @@ export class RegExp { this.nfa = Automata.toNFA(ast, flags); // find all the group marker states - gm = new Array(); + RegExp.gm = new Array(); nfaWalker(this.nfa.start, (state) => { if (state instanceof GroupStartMarkerState) { const startMarker = state as GroupStartMarkerState; if (startMarker.capturing) { - gm.push(state as GroupStartMarkerState); + RegExp.gm.push(state as GroupStartMarkerState); } } }); - this.groupMarkers = gm; + this.groupMarkers = RegExp.gm; this.flags = flags; } diff --git a/package-lock.json b/package-lock.json index 912869d..a792796 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "assemblyscript-regex", "version": "0.1.0", "license": "MIT", "devDependencies": { @@ -39,8 +38,6 @@ "dependencies": { "@as-pect/assembly": "^6.0.0", "@as-pect/core": "^6.0.0", - "@as-pect/csv-reporter": "^6.0.0", - "@as-pect/json-reporter": "^6.0.0", "chalk": "^4.1.0", "glob": "^7.1.6" }, @@ -2061,7 +2058,6 @@ "minimist": "^1.2.5", "neo-async": "^2.6.0", "source-map": "^0.6.1", - "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" }, "bin": { @@ -3102,7 +3098,239 @@ "validate-npm-package-name", "which", "worker-farm", - "write-file-atomic" + "write-file-atomic", + "agent-base", + "agentkeepalive", + "ansi-align", + "ansi-regex", + "ansi-styles", + "are-we-there-yet", + "asap", + "asn1", + "assert-plus", + "asynckit", + "aws-sign2", + "aws4", + "balanced-match", + "bcrypt-pbkdf", + "boxen", + "brace-expansion", + "buffer-from", + "builtins", + "byline", + "camelcase", + "capture-stack-trace", + "caseless", + "chalk", + "cidr-regex", + "cli-boxes", + "cliui", + "clone", + "code-point-at", + "color-convert", + "color-name", + "colors", + "combined-stream", + "concat-map", + "concat-stream", + "configstore", + "console-control-strings", + "copy-concurrently", + "core-util-is", + "create-error-class", + "cross-spawn", + "crypto-random-string", + "cyclist", + "dashdash", + "debug", + "decamelize", + "decode-uri-component", + "deep-extend", + "defaults", + "define-properties", + "delayed-stream", + "delegates", + "dot-prop", + "dotenv", + "duplexer3", + "duplexify", + "ecc-jsbn", + "emoji-regex", + "encoding", + "end-of-stream", + "env-paths", + "err-code", + "errno", + "es-abstract", + "es-to-primitive", + "es6-promise", + "es6-promisify", + "escape-string-regexp", + "execa", + "extend", + "extsprintf", + "fast-json-stable-stringify", + "flush-write-stream", + "forever-agent", + "form-data", + "from2", + "fs-minipass", + "fs.realpath", + "function-bind", + "gauge", + "genfun", + "get-caller-file", + "get-stream", + "getpass", + "global-dirs", + "got", + "har-schema", + "har-validator", + "has", + "has-flag", + "has-symbols", + "http-cache-semantics", + "http-proxy-agent", + "http-signature", + "https-proxy-agent", + "humanize-ms", + "iconv-lite", + "ignore-walk", + "import-lazy", + "ip", + "ip-regex", + "is-callable", + "is-ci", + "is-date-object", + "is-fullwidth-code-point", + "is-installed-globally", + "is-npm", + "is-obj", + "is-path-inside", + "is-redirect", + "is-regex", + "is-retry-allowed", + "is-stream", + "is-symbol", + "is-typedarray", + "isarray", + "isexe", + "isstream", + "jsbn", + "json-schema", + "json-stringify-safe", + "jsonparse", + "jsprim", + "latest-version", + "libnpmconfig", + "libnpmpublish", + "lodash._createset", + "lodash._root", + "lowercase-keys", + "make-dir", + "make-fetch-happen", + "mime-db", + "mime-types", + "minimatch", + "minimist", + "minizlib", + "ms", + "mute-stream", + "node-fetch-npm", + "npm-bundled", + "npm-logical-tree", + "npm-normalize-package-bin", + "npm-run-path", + "number-is-nan", + "oauth-sign", + "object-assign", + "object-keys", + "object.getownpropertydescriptors", + "os-homedir", + "os-tmpdir", + "p-finally", + "package-json", + "parallel-transform", + "path-exists", + "path-is-absolute", + "path-key", + "path-parse", + "performance-now", + "pify", + "prepend-http", + "process-nextick-args", + "promise-retry", + "promzard", + "proto-list", + "protoduck", + "prr", + "pseudomap", + "psl", + "pump", + "pumpify", + "punycode", + "qs", + "rc", + "registry-auth-token", + "registry-url", + "require-directory", + "require-main-filename", + "resolve-from", + "run-queue", + "safer-buffer", + "semver-diff", + "set-blocking", + "shebang-command", + "shebang-regex", + "signal-exit", + "smart-buffer", + "socks", + "socks-proxy-agent", + "spdx-correct", + "spdx-exceptions", + "spdx-expression-parse", + "spdx-license-ids", + "split-on-first", + "sshpk", + "stream-each", + "stream-iterate", + "stream-shift", + "strict-uri-encode", + "string_decoder", + "string-width", + "strip-ansi", + "strip-eof", + "strip-json-comments", + "supports-color", + "term-size", + "through", + "through2", + "timed-out", + "tough-cookie", + "tunnel-agent", + "tweetnacl", + "typedarray", + "unique-slug", + "unique-string", + "unzip-response", + "uri-js", + "url-parse-lax", + "util-deprecate", + "util-extend", + "util-promisify", + "verror", + "wcwidth", + "which-module", + "wide-align", + "widest-line", + "wrap-ansi", + "wrappy", + "xdg-basedir", + "xtend", + "y18n", + "yallist", + "yargs", + "yargs-parser" ], "dev": true, "dependencies": { @@ -3708,7 +3936,6 @@ "inBundle": true, "license": "MIT", "dependencies": { - "colors": "^1.1.2", "object-assign": "^4.1.0", "string-width": "^2.1.1" }, @@ -6998,7 +7225,6 @@ "license": "ISC", "dependencies": { "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", "read-package-json": "^2.0.0", "readdir-scoped-modules": "^1.0.0", "semver": "2 || 3 || 4 || 5", @@ -7018,7 +7244,6 @@ "license": "ISC", "dependencies": { "glob": "^7.1.1", - "graceful-fs": "^4.1.2", "json-parse-better-errors": "^1.0.1", "normalize-package-data": "^2.0.0", "npm-normalize-package-bin": "^1.0.0" @@ -7487,13 +7712,9 @@ "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "safer-buffer": "^2.0.2" }, "bin": { "sshpk-conv": "bin/sshpk-conv", diff --git a/package.json b/package.json index c73c17e..682838d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "A regex engine built with AssemblyScript", "ascMain": "assembly/index.ts", "scripts": { - "test": "npm run test:generate && npm run asbuild:untouched && npm run prettier && npm run eslint && npm run asp", + "pretest": "npm run test:generate && npm run asbuild:untouched && npm run asbuild:empty", + "test": "npm run prettier && npm run eslint && npm run asp && npm run test:empty", + "test:empty": "diff build/empty.wat assembly/__tests__/empty.wat", "test:generate": "node spec/test-generator.js", "asp": "asp --verbose --nologo", "asp:ci": "asp --nologo", @@ -14,6 +16,7 @@ "asbuild:untouched": "asc assembly/index.ts --target debug", "asbuild:optimized": "asc assembly/index.ts --target release", "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized", + "asbuild:empty": "asc --config asconfig.empty.json", "tsrun": "ts-node ts/index.ts", "benchmark": "node benchmark/benchmark.js", "eslint": "eslint --max-warnings 0 --ext ts \"assembly/**/*.ts\""