Skip to content
This repository has been archived by the owner on Aug 25, 2021. It is now read-only.

Commit

Permalink
In progress
Browse files Browse the repository at this point in the history
  • Loading branch information
PanSpagetka committed Oct 1, 2019
1 parent 6f2c274 commit b58921b
Show file tree
Hide file tree
Showing 7 changed files with 679 additions and 0 deletions.
75 changes: 75 additions & 0 deletions src/expression-editor/calculator.jison.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
export const grammar = `/* description: Parses and executes mathematical expressions. */
/* lexical grammar */
%lex
%%
\s+ /* skip whitespace */
[0-9]+("."[0-9]+)?\b return 'NUMBER';
"*" return '*';
"/" return '/';
"-" return '-';
"+" return '+';
"^" return '^';
"!" return '!';
"%" return '%';
"(" return '(';
")" return ')';
"PI" return 'PI';
"E" return 'E';
<<EOF>> return 'EOF';
. return 'INVALID';
/lex
/* operator associations and precedence */
%left '+' '-'
%left '*' '/'
%left '^'
%right '!'
%right '%'
%left UMINUS
%token INVALID
%start expressions
%% /* language grammar */
expressions
: e EOF
{ typeof console !== 'undefined' ? console.log($1) : print($1);
return $1; }
;
e
: e '+' e
{$$ = $1 + $3;}
| e '-' e
{$$ = $1 - $3;}
| e '*' e
{$$ = $1 * $3;}
| e '/' e
{$$ = $1 / $3;}
| e '^' e
{$$ = Math.pow($1, $3);}
| e '!'
{{
$$ = (function fact(n) {
return n == 0 ? 1 : fact(n - 1) * n;
})($1);
}}
| e '%'
{$$ = $1 / 100;}
| '-' e %prec UMINUS
{$$ = -$2;}
| '(' e ')'
{$$ = $2;}
| NUMBER
{$$ = Number(yytext);}
| E
{$$ = Math.E;}
| PI
{$$ = Math.PI;}
;
`
169 changes: 169 additions & 0 deletions src/expression-editor/expression-editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import React, { useState, useEffect, useReducer } from 'react';
import { parser } from './parser'
import { TypeAheadSelect } from 'patternfly-react';
import Autosuggest from 'react-autosuggest';
import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
const nearley = require("nearley");
const partialGrammar = require('./grammar.ne.js')
let partialParser = new nearley.Parser(nearley.Grammar.fromCompiled(partialGrammar));

const autocomplete = {
exp_type: ["FIELD:", "TAGS:", "COUNT OF:", "FIND:", "REGKEY:"],
entity: ["vm.","host."],
field: ['name ', 'status '],
category: ['environment ', 'category '],
operator: ['= ', 'CONTAINS '],
tag_operator: ['= ', 'CONTAINS ', ': '],
exp_operator: ['AND ', 'OR '],
value: ['value '],
check: ['CHECK ALL:', 'CHECK ANY:', 'CHECK COUNT:']
}

const getSuggestions = next => autocomplete[next] || next.map(n => autocomplete[n]).flat()

const initialState = {
inputText: "",
parsedAST: "",
partialAST: {},
isValid: true,
caretPosition: 0,
suggestions: [],
filteredSuggestions: [],
filterStr: "",
}

const reducer = (state, action) => {
console.log(action);
const { type, inputText, caretPosition, keyCode, ctrlKey } = action;
switch (type) {
case 'onKeyDown':
const { parsedAST, isValid } = parse(inputText);
let newState = {...state};
let next = [];
try {
partialParser = new nearley.Parser(nearley.Grammar.fromCompiled(partialGrammar));
let currentExp = inputText.slice(0,caretPosition)+"|";
const leftIndex = Math.max(currentExp.lastIndexOf('AND'), currentExp.lastIndexOf('OR'), 0);
console.log(currentExp.slice(leftIndex).replace(/AND|OR/,''));
partialParser.feed(currentExp.slice(leftIndex).replace(/AND|OR/,''));
next = partialParser.results[0][0].next;
console.log(next);
newState.partialAST = partialParser.results[0][0].results;
}
catch(err) {
console.log(err.message);
}
const filterStr = newState.partialAST.slice(-1)[0] ? newState.partialAST.slice(-1)[0].value : '';
console.log(filterStr);
const suggestions = getSuggestions(next);
const filteredSuggestions = suggestions.filter(x => x.toLowerCase().includes(filterStr.toLowerCase()));
return {...newState,
inputText,
parsedAST,
isValid,
caretPosition,
suggestions,
filteredSuggestions,
filterStr,
}
default:
return state;

}
}

const parse = (text, callback) => {
try {
return { parsedAST: JSON.stringify(parser.parse(text)), isValid: true};
}
catch(err) {
return { parsedAST: err.message, isValid: false};
}
}

const statusDiv = (isValid) => ((
isValid
? <div style={{backgroundColor: 'lightGreen'}}>{isValid ? 'Expression je validni' : 'Expression JE validni'}</div>
: <div style={{backgroundColor: 'red'}}>{isValid ? 'Expression je validni' : 'Expression NENI validni'}</div>
));

const generateMenu = (item) => <li style={{width: '500px', 'font-size':'14px'}}>{item}</li>

export default function ExpressionEditor() {
const [state, dispatch] = useReducer(reducer, initialState);

const onChange = (e, { newValue, method }) => {
switch (method) {
case 'type':
dispatch({
type: 'onKeyDown',
caretPosition: e.target.selectionStart,
inputText: e.target.value,
keyCode: e.keyCode,
ctrlKey: e.ctrlKey
})
break;
case 'click':
console.log('CLICK');
const leftPart = state.inputText.slice(0, state.caretPosition);
const rightPart = state.inputText.slice(state.caretPosition);
const cutedLeftPart = leftPart.slice(0, leftPart.length - state.filterStr.length);
console.log(`${cutedLeftPart}${newValue}${rightPart}`, e.target.sel);
dispatch({
type: 'onKeyDown',
caretPosition: state.caretPosition + newValue.length - state.filterStr.length,
inputText: `${cutedLeftPart}${newValue}${rightPart}`,
ctrlKey: false
})
break;
default:

}
}

// const a = (<TypeAheadSelect
// id="ee"
// options={state.suggestions}
// emptyLabel={null}
// filterBy={(x)=>(x)}
// isValid={state.isValid}
// isInvalid={!state.isValid}
// onChange={e=>console.log(e)}
// inputProps={{onKeyUp: e => dispatch({type: 'onKeyDown', caretPosition: e.target.selectionStart, inputText: e.target.value, keyCode: e.keyCode, ctrlKey: e.ctrlKey })}}
// filterBy={(option, props) => {
// return option.includes(state.filterStr)
// }}
// >
//
// </TypeAheadSelect>);
// const b = (<input
// // onChange={e => setInputText(e.target.value)}
// onKeyDown={e => dispatch({type: 'onKeyDown', caretPosition: e.target.selectionStart, inputText: e.target.value, keyCode: e.keyCode, ctrlKey: e.ctrlKey })}
// />);
console.log(state.suggestions, state.filteredSuggestions, state.filterStr);
const c = (<Autosuggest

suggestions={state.filteredSuggestions}
getSuggestionValue={x=>x}
inputProps={{value: state.inputText, onChange: (e, action) => onChange(e, action),
onKeyUp: (e, action) => dispatch({
type: 'onKeyDown',
caretPosition: e.target.selectionStart,
inputText: e.target.value,
}),
onClick: e => console.log(e.target.selectionStart), style: {width: '500px', 'font-size':'14px'}}}
renderSuggestion={generateMenu}
onSuggestionsFetchRequested={x => x}
onSuggestionSelected={ (e, { method, suggestionValue }) => onChange(e,{method, newValue: suggestionValue})}
alwaysRenderSuggestions
/>)

return (
<div>
{c}

{statusDiv(state.isValid)}
<div>{state.parsedAST}</div>
</div>
);
}
Loading

0 comments on commit b58921b

Please sign in to comment.