adds README, extends error object

This commit is contained in:
damirka 2021-09-25 12:13:59 +03:00
parent aadd998665
commit 0aa6eabb0c
12 changed files with 118 additions and 72 deletions

View File

@ -1,4 +1,4 @@
name: WASM Tests
name: WASM
on:
pull_request:
push:
@ -14,14 +14,14 @@ env:
jobs:
test-wasm-parser:
name: Test WASM on ${{ matrix.os }}
name: Test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: wasm
strategy:
matrix:
os: [macOS-latest, windows-latest, ubuntu-latest]
os: [windows-latest]
steps:
- name: Checkout
uses: actions/checkout@v2

1
Cargo.lock generated
View File

@ -1459,6 +1459,7 @@ dependencies = [
"leo-errors",
"leo-parser",
"serde",
"serde_json",
"wasm-bindgen",
]

View File

@ -84,15 +84,23 @@ impl BacktracedError {
code
}
/// Gets a unique error identifier.
pub fn error_code(&self) -> String {
format!(
"E{error_type}{code_identifier:0>3}{exit_code:0>4}",
error_type = self.error_type,
code_identifier = self.code_identifier,
exit_code = self.exit_code,
)
}
}
impl fmt::Display for BacktracedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error_message = format!(
"Error [E{error_type}{code_identifier:0>3}{exit_code:0>4}]: {message}",
error_type = self.error_type,
code_identifier = self.code_identifier,
exit_code = self.exit_code,
"Error [{error_code}]: {message}",
error_code = self.error_code(),
message = self.message,
);

View File

@ -70,6 +70,11 @@ impl FormattedError {
pub fn exit_code(&self) -> i32 {
self.backtrace.exit_code()
}
/// Returns an error identifier.
pub fn error_code(&self) -> String {
self.backtrace.error_code()
}
}
impl fmt::Display for FormattedError {
@ -96,10 +101,8 @@ impl fmt::Display for FormattedError {
let underlined = underline(self.span.col_start, self.span.col_stop);
let error_message = format!(
"Error [E{error_type}{code_identifier:0>3}{exit_code:0>4}]: {message}",
error_type = self.backtrace.error_type,
code_identifier = self.backtrace.code_identifier,
exit_code = self.backtrace.exit_code,
"Error [{error_code}]: {message}",
error_code = self.error_code(),
message = self.backtrace.message,
);

View File

@ -42,14 +42,17 @@ macro_rules! create_errors {
#[inline(always)]
fn exit_code(&self) -> i32 {
match self {
Self::FormattedError(formatted) => {
formatted.exit_code()
},
Self::BacktracedError(backtraced) => {
backtraced.exit_code()
Self::FormattedError(formatted) => formatted.exit_code(),
Self::BacktracedError(backtraced) => backtraced.exit_code()
}
}
#[inline(always)]
fn error_code(&self) -> String {
match self {
Self::FormattedError(formatted) => formatted.error_code(),
Self::BacktracedError(backtraced) => backtraced.error_code()
}
}
#[inline(always)]

View File

@ -19,6 +19,9 @@ pub trait LeoErrorCode: Sized {
/// Returns the error's exit code for the program.
fn exit_code(&self) -> i32;
/// Returns the prefixed error identifier.
fn error_code(&self) -> String;
/// Returns the error's exit code mask, as to avoid conflicts.
fn exit_code_mask() -> i32;

View File

@ -109,6 +109,24 @@ pub enum LeoError {
}
impl LeoError {
/// Implement error code for each type of Error. For the unsupported use a default value.
pub fn error_code(&self) -> String {
use LeoError::*;
match self {
AsgError(error) => error.error_code(),
AstError(error) => error.error_code(),
CliError(error) => error.error_code(),
CompilerError(error) => error.error_code(),
ImportError(error) => error.error_code(),
InputError(_error) => Default::default(), // TODO migrate me, or not cause we want inputs to have 0 deps.
PackageError(error) => error.error_code(),
ParserError(error) => error.error_code(),
SnarkVMError(_error) => Default::default(), // TODO update once snarkvm implments a global top level error similar to LeoError.
StateError(error) => error.error_code(),
}
}
/// Implment exit code for each type of Error, even the ones that don't have one.
pub fn exit_code(&self) -> i32 {
use LeoError::*;

View File

@ -45,8 +45,12 @@ path = "../ast-passes"
version = "1.0"
features = [ "derive" ]
[dependencies.serde_json]
version = "1.0"
[dependencies.wasm-bindgen]
version = "0.2"
features = [ "serde-serialize" ]
# Crate metadata

34
wasm/README.md Normal file
View File

@ -0,0 +1,34 @@
# Leo WASM
<!-- [![Crates.io](https://img.shields.io/crates/v/leo-wasm.svg?color=neon)](https://crates.io/crates/leo-wasm) -->
[![Authors](https://img.shields.io/badge/authors-Aleo-orange.svg)](../AUTHORS)
[![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE.md)
This directory contains WASM bindings for the Leo compiler.
## Limitations
Currently, WASM target of the compiler supports parsing and canonicalization stages.
## API
This is a list of the supported methods and their signatures.
### leo.parse
Method takes in a Leo program as string and returns JSON string with the resulting AST or throws a LeoError.
```ts
export interface LeoError {
text: string, // Full error text (including span)
code: string, // Leo error identifier (e.g. "EPAR0370005")
exitCode: number // Exit code for an error (e.g. 370005)
}
/**
* @param {String} program Leo program text to parse and produce AST
* @return {String} Resulting AST as a JSON string.
* @throws {LeoError} When program contains invalid Leo code.
*/
export function parse(program: string): string;
```

View File

@ -18,12 +18,21 @@
// which is not wasm compatible. All compiler passes (such as TypeInference)
use leo_ast::AstPass;
use leo_errors::LeoError;
use serde_json::json;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export interface LeoError { text: string, code: string, exitCode: number }
"#;
/// Publicly accessible method.
/// Parse the code and return an AST as JSON or an error object. asd
#[wasm_bindgen(method, catch)]
pub fn parse(program: &str) -> Result<String, JsValue> {
Ok(parse_program(program).map_err(|err| JsValue::from_str(&err.to_string()))?)
Ok(parse_program(program).map_err(error_to_value)?)
}
/// Parse the program and pass the Canonicalization phase;
@ -35,3 +44,13 @@ fn parse_program(program: &str) -> leo_errors::Result<String> {
Ok(ast)
}
/// Make a pretty-print JS object for the thrown error.
fn error_to_value(err: LeoError) -> JsValue {
JsValue::from_serde(&json!({
"error": err.to_string(),
"code": err.error_code(),
"exitCode": err.exit_code()
}))
.expect("Unable to create an error object from JSON")
}

View File

@ -1,59 +1,8 @@
{
"name": "parser-wasm-tests",
"version": "0.1.0",
"lockfileVersion": 2,
"lockfileVersion": 1,
"requires": true,
"packages": {
"": {
"name": "parser-wasm-tests",
"version": "0.1.0",
"license": "GNU",
"dependencies": {
"@types/js-yaml": "^4.0.3",
"@types/node": "^16.9.4",
"js-yaml": "^4.1.0",
"typescript": "^3.9.10"
}
},
"node_modules/@types/js-yaml": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.3.tgz",
"integrity": "sha512-5t9BhoORasuF5uCPr+d5/hdB++zRFUTMIZOzbNkr+jZh3yQht4HYbRDyj9fY8n2TZT30iW9huzav73x4NikqWg=="
},
"node_modules/@types/node": {
"version": "16.9.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.4.tgz",
"integrity": "sha512-KDazLNYAGIuJugdbULwFZULF9qQ13yNWEBFnfVpqlpgAAo6H/qnM9RjBgh0A0kmHf3XxAKLdN5mTIng9iUvVLA=="
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/typescript": {
"version": "3.9.10",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
}
},
"dependencies": {
"@types/js-yaml": {
"version": "4.0.3",

View File

@ -16,7 +16,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as parser from '../../pkg/leo_wasm';
import * as leo from '../../pkg/leo_wasm';
import * as yaml from 'js-yaml';
// Path to the parser tests folder
@ -100,7 +100,7 @@ function test(filePath: string, outFile: string|null): TestResult[] {
// and collect outputs to later compare to saved .out expectations.
for (const sample of samples) {
try {
outputs.push(JSON.parse(parser.parse(sample))); // Parser outputs JSON
outputs.push(JSON.parse(leo.parse(sample))); // Parser outputs JSON
if (expectation === Expectation.Fail) { // If expectation was Fail and it passed
mismatches.push({
filePath,
@ -110,6 +110,10 @@ function test(filePath: string, outFile: string|null): TestResult[] {
});
}
} catch (error) {
console.log(error.code);
console.log(error.exitCode);
outputs.push(error.toString());
if (expectation === Expectation.Pass) { // If expectation was Pass and it failed
mismatches.push({