npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

lavats

v1.0.3

Published

lavats是一个由typescript写成的支持umd的DSL式词法分析器、语法分析器库。

Downloads

6

Readme

lavats

lavats是一个由typescript写成的支持umdDSL式的词法分析器、语法分析器库。

特色

  • 支持typescript*.d.ts文件
  • 支持umd,可运行在浏览器nodejs环境下
  • 使用DSL式,可与js语言无缝衔接
  • 支持左递归循环

安装

在命令行中输入:

npm install lavats

引入

cmd

var lavats = require("lavats");

amd

require(["lavats"], function(lavats) {

})
define(["lavats"], function(lavats) {

})

es6

import * as lavats from "lavats";

<script>

<script src="./node_modules/lavats/dist/index.js"></script>
<script>

lavats

</script>

使用

此处介绍了一些常用的类型,其他的具体类型信息,可以查看包内的*.d.ts文件。

TerminalRule

用于生成终止符的规则

import { TerminalRule } from "lavats";
// new TerminalRule(options: TerminalOptions | RegExp | string);

// 可以接受正则表达式(不支持flags)
new TerminalRule(/[0-9]+/);

// 可以接受字符串
new TerminalRule("+");

// 可以接受TerminalOptions
// interface TerminalOptions {
//     reg: RegExp | string;        // 正则表达式或字符串
//     ignore?: boolean;            // 是否忽略,默认为false
//     ast?: typeof TerminalAst;    // 生成的ast类型,默认为TerminalAst
// }
new TerminalRule({ reg: /\s+/, ignore: true });

Lex

用于生成进行词法分析的Lex对象

  • 一般用例:
import { Lex, TerminalRule } from "lavats";

// new Lex(terminalRules: TerminalRule[])

let lex = new Lex([
    new TerminalRule(/[0-9]+/),
    new TerminalRule("+"),
    new TerminalRule("-"),
    new TerminalRule("*"),
    new TerminalRule("/"),
    new TerminalRule({ reg: /\s+/, ignore: true })
]);

let result = lex.match("100 * 100");

// interface LexResult {
//     ast: TerminalAst[];          // ast数组
//     errors: Token[];             // 未能识别的token
//     ignore: TerminalAst[];       // 被忽略的ast
// }

JSON.stringify(result); 
// =>
// '{
//     "ast": [
//         { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 0 }, "end": { "row": 0, "col": 3 } } },
//         { "name": "TerminalAst", "reg": "/\\*/", "token": { "content": "*", "start": { "row": 0, "col": 4 }, "end": { "row": 0, "col": 5 } } },
//         { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 6 }, "end": { "row": 0, "col": 9 } } }
//     ],
//     "errors": [],
//     "ignore": [
//         { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 3 }, "end": { "row": 0, "col": 4 } } },
//         { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 5 }, "end": { "row": 0, "col": 6 } } }
//     ]
// }'
  • 含有无法识别的字符时:
import { Lex, TerminalRule } from "lavats";

let lex = new Lex([
    new TerminalRule(/[0-9]+/),
    new TerminalRule("+"),
    new TerminalRule("-"),
    new TerminalRule("*"),
    new TerminalRule("/"),
    new TerminalRule({ reg: /\s+/, ignore: true })
]);

let result = lex.match("100.00 * 100");

JSON.stringify(result); 
// =>
// '{
//     "ast": [
//         { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 0 }, "end": { "row": 0, "col": 3 } } },
//         // 此处多出了'00'
//         { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "00", "start": { "row": 0, "col": 4 }, "end": { "row": 0, "col": 6 } } },
//         { "name": "TerminalAst", "reg": "/\\*/", "token": { "content": "*", "start": { "row": 0, "col": 7 }, "end": { "row": 0, "col": 8 } } },
//         { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 9 }, "end": { "row": 0, "col": 12 } } }
//     ],
//     "errors": [
//         // 此处多出了'.'
//         { "content": ".", "start": { "row": 0, "col": 3 }, "end": { "row": 0, "col": 4 } }
//     ], "ignore": [
//         { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 6 }, "end": { "row": 0, "col": 7 } } },
//         { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 8 }, "end": { "row": 0, "col": 9 } } }
//     ]
// }'

StreamLex

用于生成进行词法分析的Lex对象,接受流式数据,以事件形式返回结果

import { StreamLex, TerminalRule } from "lavats";

// new StreamLex(rules: TerminalRule[])

let lex = new StreamLex([
    new TerminalRule(/[0-9]+/),
    new TerminalRule("+"),
    new TerminalRule("-"),
    new TerminalRule("*"),
    new TerminalRule("/"),
    new TerminalRule({ reg: /\s+/, ignore: true })
]);

lex.on("ast", ast => console.log("ast:\n" + JSON.stringify(ast, null, 4)));
lex.on("error", token => console.log("error:\n" + JSON.stringify(token, null, 4)));
lex.on("ignore", ast => console.log("ignore:\n" + JSON.stringify(ast, null, 4)));
lex.on("end", () => console.log("end"));

lex.match("100.00");
// ast:
// { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 0 }, "end": { "row": 0, "col": 3 } } }

// error:
// { "content": ".", "start": { "row": 0, "col": 3 }, "end": { "row": 0, "col": 4 } }

// warning:此处并没有返回'00',因为并不知道'00'后面是否有其他的数字

lex.match(" *");
// ast:
// { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "00", "start": { "row": 0, "col": 4 }, "end": { "row": 0, "col": 6 } } }

// ignore:
// { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 6 }, "end": { "row": 0, "col": 7 } } }

lex.match(" 100");
// ast:
// { "name": "TerminalAst", "reg": "/\\*/", "token": { "content": "*", "start": { "row": 0, "col": 7 }, "end": { "row": 0, "col": 8 } } }

// ignore:
// { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 8 }, "end": { "row": 0, "col": 9 } } }

// warning:此处也并没有返回'100',理由同上

lex.end();
// ast:
// { "name": "TerminalAst", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 9 }, "end": { "row": 0, "col": 12 } } }

// end
  • 另有reset方法,用于重置StreamLex对象
lex.reset();

RuleCollection

用于生成语法分析器的对象

import { RuleCollection, TerminalAst, DelayAst } from "lavats";

class Num extends TerminalAst {
    exec() {
        return +this.token.content;
    }
}

class Add extends TerminalAst {
    exec(left, right) {
        return left + right;
    }
}

class Sub extends TerminalAst {
    exec(left, right) {
        return left - right;
    }
}

class Mul extends TerminalAst {
    exec(left, right) {
        return left * right;
    }
}

class Div extends TerminalAst {
    exec(left, right) {
        return left / right;
    }
}

class Expr extends DelayAst {
    get left() {
        return this.children[0];
    }
    get operator() {
        return this.children[1];
    }
    get right() {
        return this.children[2];
    }
    exec() {
        return this.operator.exec(this.left.exec(), this.right.exec());
    }
}

let collection = new RuleCollection();

// 省略空格
collection.terminal({ reg: /\s+/, ignore: true });

// num => [0-9]+
let num = collection.terminal({
    reg: /[0-9]+/,
    ast: Num
});

// add => "+"
let add = collection.terminal({
    reg: "+",
    ast: Add
})

// sub => "-"
let sub = collection.terminal({
    reg: "-",
    ast: Sub
})

// mul => "*"
let mul = collection.terminal({
    reg: "*",
    ast: Mul
})

// div => "/"
let div = collection.terminal({
    reg: "/",
    ast: Div
})

// 无特殊Ast对象的规则可以直接定义中间变量使用
// operator => add | sub | mul | div
let operator = add.or(sub).or(mul).or(div);

// 非终结符规则的定义需要分开两步(先声明,后定义)
let expr = collection.delay(Expr);
// expr => num operator num
expr.define(num.and(operator).and(num));

// 获取语法分析器Parser
let parser = collection.getParser(expr);
let result = parser.match("100 * 100");

// interface ParserResult<T> {
//     ast?: T;                             // 生成的ast树,可通过判断是否为undefined,来判断是否成功
//     ignoreAst: TerminalAst[];            // 被跳过的ast
//     errorAst: Ast[];                     // 不符合语法规则的ast
//     errorToken: Token[];                 // 不符合词法规则的token
// }

console.log(JSON.stringify(result));
// {
//     "ast": {
//         "name": "Expr",
//         "children": [
//             { "name": "Num", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 0 }, "end": { "row": 0, "col": 3 } } },
//             { "name": "Mul", "reg": "/\\*/", "token": { "content": "*", "start": { "row": 0, "col": 4 }, "end": { "row": 0, "col": 5 } } },
//             { "name": "Num", "reg": "/[0-9]+/", "token": { "content": "100", "start": { "row": 0, "col": 6 }, "end": { "row": 0, "col": 9 } } }
//         ]
//     },
//     "ignoreAst": [
//         { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 3 }, "end": { "row": 0, "col": 4 } } },
//         { "name": "TerminalAst", "reg": "/\\s+/", "token": { "content": " ", "start": { "row": 0, "col": 5 }, "end": { "row": 0, "col": 6 } } }
//     ],
//     "errorAst": [],
//     "errorToken": []
// }

let execResult = result.ast.exec();
console.log(execResult);        // 10000

另外有StreamParser,接受流式数据,以事件形式返回结果

let parser = collection.getStreamParser(expr);
parser.on("error", result => result);
parser.on("ignore", result => result);
parser.on("success", result => result);
parser.on("fail", result => result);
parser.on("end", result => {
    let execResult = result.ast.exec();
    console.log(execResult);
});

parser.match("100 + 100");
parser.end();
// 200

提供便捷方法,可使用插值字符串的方式,来定义规则。

// operator => add | sub | mul | div
// let operator = add.or(sub).or(mul).or(div);
// 可换写成:
let operator = Rule.bnf`${add} | ${sub} | ${mul} | ${div}`;

// expr => num operator num
// expr.define(num.and(operator).and(num));
// 可换写成:
expr.defineBnf`${num} ${operator} ${num}`;

StreamParser可通过reset方法重置

parser.reset();