feat: add pylyzer_wasm crate

This commit is contained in:
Shunsuke Shibayama 2024-08-20 14:19:11 +09:00
parent 6828ddcf56
commit b6a368257f
6 changed files with 381 additions and 2 deletions

71
Cargo.lock generated
View file

@ -85,6 +85,12 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
version = "1.5.0"
@ -583,6 +589,16 @@ dependencies = [
"rustpython-parser",
]
[[package]]
name = "pylyzer_wasm"
version = "0.0.60"
dependencies = [
"erg_common",
"erg_compiler",
"pylyzer_core",
"wasm-bindgen",
]
[[package]]
name = "quote"
version = "1.0.36"
@ -977,6 +993,61 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.74",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.74",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
name = "windows-targets"
version = "0.52.6"

View file

@ -13,6 +13,7 @@ repository.workspace = true
members = [
"crates/py2erg",
"crates/pylyzer_core",
"crates/pylyzer_wasm",
]
[workspace.package]

View file

@ -6,8 +6,8 @@ use erg_common::traits::{ExitStatus, New, Runnable, Stream};
use erg_common::Str;
use erg_compiler::artifact::{BuildRunnable, Buildable, CompleteArtifact, IncompleteArtifact};
use erg_compiler::build_package::GenericPackageBuilder;
use erg_compiler::context::ModuleContext;
use erg_compiler::erg_parser::ast::{Module, AST};
use erg_compiler::context::{Context, ContextProvider, ModuleContext};
use erg_compiler::erg_parser::ast::{Module, VarName, AST};
use erg_compiler::erg_parser::build_ast::ASTBuildable;
use erg_compiler::erg_parser::error::{
CompleteArtifact as ParseArtifact, IncompleteArtifact as IncompleteParseArtifact, ParseErrors,
@ -16,6 +16,7 @@ use erg_compiler::erg_parser::error::{
use erg_compiler::erg_parser::parse::Parsable;
use erg_compiler::error::{CompileError, CompileErrors};
use erg_compiler::module::SharedCompilerResource;
use erg_compiler::varinfo::VarInfo;
use erg_compiler::GenericHIRBuilder;
use py2erg::{dump_decl_package, ShadowingMode};
use rustpython_ast::source_code::{RandomLocator, SourceRange};
@ -140,6 +141,18 @@ impl New for PythonAnalyzer {
}
}
impl ContextProvider for PythonAnalyzer {
fn dir(&self) -> erg_common::dict::Dict<&VarName, &VarInfo> {
self.checker.dir()
}
fn get_receiver_ctx(&self, receiver_name: &str) -> Option<&Context> {
self.checker.get_receiver_ctx(receiver_name)
}
fn get_var_info(&self, name: &str) -> Option<(&VarName, &VarInfo)> {
self.checker.get_var_info(name)
}
}
impl Runnable for PythonAnalyzer {
type Err = CompileError;
type Errs = CompileErrors;

View file

@ -0,0 +1,19 @@
[package]
name = "pylyzer_wasm"
description = "Wasm wrapper for pylyzer"
version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
publish = false
[dependencies]
wasm-bindgen = "0.2"
erg_common = { workspace = true }
erg_compiler = { workspace = true }
pylyzer_core = { version = "*", path = "../pylyzer_core" }
[lib]
crate-type = ["cdylib", "rlib"]
path = "lib.rs"

View file

@ -0,0 +1,13 @@
# pylyzer_wasm
Wasm wrapper for pylyzer.
## Usage
```ts
import { Analyzer } from 'pylyzer_wasm';
const analyzer = new Analyzer();
const errors = analyzer.check('print("Hello, World!")');
const locals = analyzer.dir();
```

262
crates/pylyzer_wasm/lib.rs Normal file
View file

@ -0,0 +1,262 @@
use wasm_bindgen::prelude::*;
use erg_common::error::ErrorCore;
use erg_common::error::Location as Loc;
use erg_common::traits::{Runnable, Stream};
use erg_compiler::context::ContextProvider;
use erg_compiler::erg_parser::ast::VarName;
use erg_compiler::error::CompileError;
use erg_compiler::ty::Type as Ty;
use erg_compiler::varinfo::VarInfo;
use pylyzer_core::PythonAnalyzer;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[wasm_bindgen]
pub enum CompItemKind {
Method = 0,
Function = 1,
Constructor = 2,
Field = 3,
Variable = 4,
Class = 5,
Struct = 6,
Interface = 7,
Module = 8,
Property = 9,
Event = 10,
Operator = 11,
Unit = 12,
Value = 13,
Constant = 14,
Enum = 15,
EnumMember = 16,
Keyword = 17,
Text = 18,
Color = 19,
File = 20,
Reference = 21,
Customcolor = 22,
Folder = 23,
TypeParameter = 24,
User = 25,
Issue = 26,
Snippet = 27,
}
#[derive(Debug, Clone)]
#[wasm_bindgen]
pub struct Location(Loc);
impl From<Loc> for Location {
fn from(loc: Loc) -> Self {
Self(loc)
}
}
impl Location {
pub const UNKNOWN: Location = Location(Loc::Unknown);
}
#[derive(Debug, Clone)]
#[wasm_bindgen]
#[allow(dead_code)]
pub struct Type(Ty);
#[derive(Debug, Clone)]
#[wasm_bindgen]
pub struct VarEntry {
name: VarName,
vi: VarInfo,
}
impl VarEntry {
pub fn new(name: VarName, vi: VarInfo) -> Self {
Self { name, vi }
}
}
#[wasm_bindgen]
impl VarEntry {
pub fn name(&self) -> String {
self.name.to_string()
}
pub fn item_kind(&self) -> CompItemKind {
match &self.vi.t {
Ty::Callable { .. } => CompItemKind::Function,
Ty::Subr(subr) => {
if subr.self_t().is_some() {
CompItemKind::Method
} else {
CompItemKind::Function
}
}
Ty::Quantified(quant) => match quant.as_ref() {
Ty::Callable { .. } => CompItemKind::Function,
Ty::Subr(subr) => {
if subr.self_t().is_some() {
CompItemKind::Method
} else {
CompItemKind::Function
}
}
_ => unreachable!(),
},
Ty::ClassType => CompItemKind::Class,
Ty::TraitType => CompItemKind::Interface,
Ty::Poly { name, .. } if &name[..] == "Module" => CompItemKind::Module,
_ if self.vi.muty.is_const() => CompItemKind::Constant,
_ => CompItemKind::Variable,
}
}
pub fn typ(&self) -> String {
self.vi.t.to_string()
}
}
#[wasm_bindgen]
impl Location {
pub fn ln_begin(&self) -> Option<u32> {
self.0.ln_begin()
}
pub fn ln_end(&self) -> Option<u32> {
self.0.ln_end()
}
pub fn col_begin(&self) -> Option<u32> {
self.0.col_begin()
}
pub fn col_end(&self) -> Option<u32> {
self.0.col_end()
}
}
#[derive(Debug, Clone)]
#[wasm_bindgen(getter_with_clone)]
pub struct Error {
pub errno: usize,
pub is_warning: bool,
// pub kind: ErrorKind,
pub loc: Location,
pub desc: String,
pub hint: Option<String>,
}
fn find_fallback_loc(err: &ErrorCore) -> Loc {
if err.loc == Loc::Unknown {
for sub in &err.sub_messages {
if sub.loc != Loc::Unknown {
return sub.loc;
}
}
Loc::Unknown
} else {
err.loc
}
}
impl From<CompileError> for Error {
fn from(err: CompileError) -> Self {
let loc = Location(find_fallback_loc(&err.core));
let sub_msg = err
.core
.sub_messages
.first()
.map(|sub| {
sub.msg
.iter()
.fold("\n".to_string(), |acc, s| acc + s + "\n")
})
.unwrap_or_default();
let desc = err.core.main_message + &sub_msg;
Self {
errno: err.core.errno,
is_warning: err.core.kind.is_warning(),
// kind: err.kind(),
loc,
desc,
hint: err
.core
.sub_messages
.first()
.and_then(|sub| sub.hint.clone()),
}
}
}
impl Error {
pub const fn new(
errno: usize,
is_warning: bool,
loc: Location,
desc: String,
hint: Option<String>,
) -> Self {
Self {
errno,
is_warning,
loc,
desc,
hint,
}
}
}
#[wasm_bindgen]
// #[derive()]
pub struct Analyzer {
analyzer: PythonAnalyzer,
}
impl Default for Analyzer {
fn default() -> Self {
Self::new()
}
}
#[wasm_bindgen]
impl Analyzer {
pub fn new() -> Self {
Analyzer {
analyzer: PythonAnalyzer::default(),
}
}
pub fn clear(&mut self) {
self.analyzer.clear();
}
pub fn start_message(&self) -> String {
self.analyzer.start_message()
}
pub fn dir(&mut self) -> Box<[VarEntry]> {
self.analyzer
.dir()
.into_iter()
.map(|(n, vi)| VarEntry::new(n.clone(), vi.clone()))
.collect::<Vec<_>>()
.into_boxed_slice()
}
pub fn check(&mut self, input: &str) -> Box<[Error]> {
match self.analyzer.analyze(input.to_string(), "exec") {
Ok(artifact) => artifact
.warns
.into_iter()
.map(Error::from)
.collect::<Vec<_>>()
.into_boxed_slice(),
Err(mut err_artifact) => {
err_artifact.errors.extend(err_artifact.warns);
let errs = err_artifact
.errors
.into_iter()
.map(Error::from)
.collect::<Vec<_>>();
errs.into_boxed_slice()
}
}
}
}