-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adding javascript basic parser (#45)
* Added js basic parser * Deleted lib files / updated package.json name * Added workflow / updated project npm name * Updated workflow to install antlr4 * Updated makefile / antlr4 js build * Added missing uvl grammar js * Changed route on makefile for js * Added call to make dev in workflow * Update version 0.0.3 * Change env variable name --------- Co-authored-by: vlamas <vlamas@udc.es>
- Loading branch information
Showing
16 changed files
with
445 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
name: Build and Deploy JS Parser | ||
|
||
on: | ||
push | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v2 | ||
|
||
- name: Setup Node | ||
uses: actions/setup-node@v1 | ||
with: | ||
node-version: 19 | ||
registry-url: https://registry.npmjs.org/ | ||
|
||
- name: Install ANTLR4 | ||
run: | | ||
make dev | ||
make js_parser | ||
- name: Generate and Build Node Code | ||
run: cd js && npm install && npm run build-grammar | ||
|
||
- name: Publish npm package | ||
run: cd js && npm publish --access public | ||
env: | ||
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,6 @@ | |
.vscode | ||
env | ||
python/uvl/__pycache__ | ||
uvl/.antlr | ||
uvl/.antlr | ||
js/node_modules | ||
js/package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
eslint.config.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# UVL Parser Javascript | ||
|
||
This is a parser for the UVL (Unified Variability Language) written in Javascript. The parser is based on the ANTLR4 grammar for UVL. | ||
|
||
index | content | ||
--- | --- | ||
[Installation](#installation) | How to install the parser | ||
[Usage](#usage) | How to use the parser | ||
[Development](#development) | How to develop the parser | ||
[Publishing in npm](#publishing-in-npm) | How to publish the parser in npm | ||
|
||
## Installation | ||
|
||
```bash | ||
npm install uvl-parser | ||
``` | ||
|
||
## Usage | ||
|
||
### ES6 | ||
|
||
```javascript | ||
import { FeatureModel } from 'uvl-parser'; | ||
const featureModel = new FeatureModel('file.uvl'); | ||
const tree = featureModel.getFeatureModel(); | ||
``` | ||
|
||
## Development | ||
|
||
### Install dependencies | ||
|
||
```bash | ||
npm install | ||
``` | ||
|
||
### Build grammar | ||
|
||
```bash | ||
npm run build-grammar | ||
``` | ||
|
||
### Run tests | ||
|
||
```bash | ||
npm test | ||
``` | ||
|
||
## Publishing in npm | ||
|
||
The run build will create the folder with the compiled code. To publish in npm, run the following commands: | ||
|
||
```bash | ||
npm run build | ||
npm publish --access public | ||
``` | ||
|
||
## Authors | ||
|
||
- Victor Lamas: [victor.lamas@udc.es](mailto:victor.lamas@udc.es) | ||
- Maria Isabel Limaylla: [maria.limaylla@udc.es](mailto:maria.limaylla@udc.es) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import globals from "globals"; | ||
import pluginJs from "@eslint/js"; | ||
|
||
|
||
export default [ | ||
{ | ||
languageOptions: { globals: globals.browser } | ||
}, | ||
pluginJs.configs.recommended, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import FeatureModel from './src/FeatureModel.js'; | ||
import UVLJavaScriptParser from './src/lib/UVLJavaScriptParser.js'; | ||
|
||
export { UVLJavaScriptParser, FeatureModel }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "uvl-parser", | ||
"main": "index.js", | ||
"type": "module", | ||
"version": "0.0.3", | ||
"scripts": { | ||
"build-grammar": "antlr4 -Dlanguage=JavaScript -o src/lib -Xexact-output-dir ../uvl/UVLJavaScript.g4", | ||
"lint": "eslint src --ignore-pattern src/lib/*", | ||
"test": "vitest" | ||
}, | ||
"devDependencies": { | ||
"@eslint/js": "^9.8.0", | ||
"@rollup/plugin-node-resolve": "^15.2.3", | ||
"eslint": "^9.8.0", | ||
"globals": "^15.8.0", | ||
"vitest": "^2.0.4" | ||
}, | ||
"dependencies": { | ||
"antlr4": "4.12.0" | ||
}, | ||
"author": { | ||
"name": "Maria Isabel Limaylla", | ||
"email": "maria.limaylla@udc.es" | ||
}, | ||
"contributors": [ | ||
{ | ||
"name": "Victor Lamas", | ||
"email": "victor.lamas@udc.es" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
|
||
import UVLJavaScriptCustomLexer from './UVLJavaScriptCustomLexer.js'; | ||
import UVLJavaScriptParser from './lib/UVLJavaScriptParser.js'; | ||
import ErrorListener from "./errors/ErrorListener.js"; | ||
import antlr4 from 'antlr4'; | ||
import fs from 'fs'; | ||
|
||
export { UVLJavaScriptParser }; | ||
|
||
export default class FeatureModel { | ||
|
||
constructor(param) { | ||
this.featureModel = ''; | ||
let chars = ''; | ||
if (this.isFile(param)) { | ||
chars = new antlr4.FileStream(param); | ||
} else { | ||
chars = antlr4.CharStreams.fromString(param); | ||
} | ||
this.getTree(chars); | ||
} | ||
|
||
isFile(str) { | ||
try { | ||
return fs.statSync(str); | ||
} catch (e) { | ||
console.error('Error: ' + e); | ||
return false; | ||
} | ||
} | ||
|
||
getTree(chars) { | ||
const lexer = new UVLJavaScriptCustomLexer(chars); | ||
const tokens = new antlr4.CommonTokenStream(lexer); | ||
const errorListener = new ErrorListener(); | ||
let parser = new UVLJavaScriptParser(tokens); | ||
parser.removeErrorListeners(); | ||
parser.addErrorListener(errorListener); | ||
const tree = parser.featureModel(); | ||
this.featureModel = tree; | ||
} | ||
|
||
getFeatureModel() { | ||
return this.featureModel; | ||
} | ||
|
||
toString() { | ||
return this.featureModel.getText(); | ||
} | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import UVLJavaScriptLexer from './lib/UVLJavaScriptLexer.js'; | ||
import UVLJavaScriptParser from './lib/UVLJavaScriptParser.js'; | ||
import antlr4 from 'antlr4'; | ||
|
||
export default class UVLJavaScriptCustomLexer extends UVLJavaScriptLexer { | ||
|
||
constructor(input_stream) { | ||
super(input_stream); | ||
this.tokens = []; | ||
this.indents = []; | ||
this.opened = 0; | ||
this.lastToken = null; | ||
} | ||
|
||
emitToken(t) { | ||
super.emitToken(t); | ||
this.tokens.push(t); | ||
} | ||
|
||
nextToken() { | ||
if (this._input.LA(1) === antlr4.Token.EOF && this.indents.length !== 0) { | ||
while (this.tokens.length > 0 && this.tokens[this.tokens.length - 1].type === antlr4.Token.EOF) { | ||
this.tokens.pop(); | ||
} | ||
|
||
this.emitToken(this.commonToken(UVLJavaScriptLexer.NEWLINE, "\n")); | ||
|
||
while (this.indents.length !== 0) { | ||
this.emitToken(this.createDedent()); | ||
this.indents.pop(); | ||
} | ||
|
||
this.emitToken(this.commonToken(antlr4.Token.EOF, "<EOF>")); | ||
} | ||
|
||
const nextToken = super.nextToken(); | ||
|
||
if (nextToken.channel === antlr4.Token.DEFAULT_CHANNEL) { | ||
this.lastToken = nextToken; | ||
} | ||
|
||
return this.tokens.length > 0 ? this.tokens.shift() : nextToken; | ||
} | ||
|
||
createDedent() { | ||
const dedent = this.commonToken(UVLJavaScriptLexer.DEDENT, ""); | ||
dedent.line = this.lastToken.line; | ||
return dedent; | ||
} | ||
|
||
commonToken(type, text) { | ||
const stop = this.getCharIndex() - 1; | ||
const start = text ? stop - text.length + 1 : stop; | ||
return new antlr4.CommonToken(this._tokenFactorySourcePair, type, antlr4.Token.DEFAULT_CHANNEL, start, stop); | ||
} | ||
|
||
static getIndentationCount(spaces) { | ||
let count = 0; | ||
for (const ch of spaces) { | ||
if (ch === '\t') { | ||
count += 8 - (count % 8); | ||
} else { | ||
count += 1; | ||
} | ||
} | ||
return count; | ||
} | ||
|
||
skipToken() { | ||
this.skip(); | ||
} | ||
|
||
atStartOfInput() { | ||
return this._interp.column === 0 && this._interp.line === 1; | ||
} | ||
|
||
handleNewline() { | ||
const newLine = this._interp.getText(this._input).replace(/[^\r\n\f]+/g, ""); | ||
const spaces = this._interp.getText(this._input).replace(/[\r\n\f]+/g, ""); | ||
const next = String.fromCharCode(this._input.LA(1)); | ||
|
||
if (this.opened > 0 || next === '\r' || next === '\n' || next === '\f' || next === '#') { | ||
this.skip(); | ||
} else { | ||
this.emitToken(this.commonToken(UVLJavaScriptLexer.NEWLINE, newLine)); | ||
|
||
const indent = UVLJavaScriptCustomLexer.getIndentationCount(spaces); | ||
const previous = this.indents.length === 0 ? 0 : this.indents[this.indents.length - 1]; | ||
|
||
if (indent === previous) { | ||
this.skip(); | ||
} else if (indent > previous) { | ||
this.indents.push(indent); | ||
this.emitToken(this.commonToken(UVLJavaScriptParser.INDENT, spaces)); | ||
} else { | ||
while (this.indents.length > 0 && this.indents[this.indents.length - 1] > indent) { | ||
this.emitToken(this.createDedent()); | ||
this.indents.pop(); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import antlr4 from "antlr4"; | ||
|
||
import SyntaxGenericError from "./SyntaxGenericError.js"; | ||
|
||
/** | ||
* Custom Error Listener | ||
* | ||
* @returns {object} | ||
*/ | ||
class ErrorListener extends antlr4.error.ErrorListener { | ||
/** | ||
* Checks syntax error | ||
* | ||
* @param {object} recognizer The parsing support code essentially. Most of it is error recovery stuff | ||
* @param {object} symbol Offending symbol | ||
* @param {int} line Line of offending symbol | ||
* @param {int} column Position in line of offending symbol | ||
* @param {string} message Error message | ||
* @param {string} payload Stack trace | ||
*/ | ||
syntaxError(recognizer, symbol, line, column, message) { | ||
throw new SyntaxGenericError({ line, column, message }); | ||
} | ||
} | ||
|
||
export default ErrorListener; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
export default class SyntaxGenericError extends Error { | ||
constructor(payload) { | ||
super(payload); | ||
|
||
this.code = "E_SYNTAX_GENERIC"; | ||
this.message = "Something went wrong"; | ||
|
||
if (typeof payload !== "undefined") { | ||
this.message = payload.message || this.message; | ||
this.payload = payload; | ||
} | ||
|
||
console.error( | ||
`line ${this.payload.line}, col ${this.payload.column}: ${this.payload.message}`, | ||
); | ||
Error.captureStackTrace(this, SyntaxGenericError); | ||
} | ||
} |
Oops, something went wrong.