Implement SyntaxTree visualization

This commit is contained in:
IRBorisov 2023-08-01 21:55:18 +03:00
parent a8bbb2b63c
commit c7d4902137
10 changed files with 140 additions and 15 deletions

Binary file not shown.

View File

@ -33,7 +33,7 @@ function Modal({ title, hideWindow, onSubmit, onCancel, canSubmit, children, sub
</div> </div>
<div ref={ref} className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col w-fit h-fit z-[60] clr-card border shadow-md mb-[5rem]'> <div ref={ref} className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col w-fit h-fit z-[60] clr-card border shadow-md mb-[5rem]'>
{ title && <h1 className='mb-2 text-xl font-bold text-center'>{title}</h1> } { title && <h1 className='mb-2 text-xl font-bold text-center'>{title}</h1> }
<div className='py-2'> <div>
{children} {children}
</div> </div>
<div className='flex justify-between w-full pt-4 mt-2 border-t-4'> <div className='flex justify-between w-full pt-4 mt-2 border-t-4'>

View File

@ -36,6 +36,7 @@ function DlgCreateCst({ hideWindow, defaultType, onCreate }: DlgCreateCstProps)
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<ConceptSelect <ConceptSelect
className='my-4'
options={CstTypeSelector} options={CstTypeSelector}
placeholder='Выберите тип' placeholder='Выберите тип'
values={selectedType ? [{ value: selectedType, label: getCstTypeLabel(selectedType) }] : []} values={selectedType ? [{ value: selectedType, label: getCstTypeLabel(selectedType) }] : []}

View File

@ -1,27 +1,68 @@
import { useMemo } from 'react';
import { darkTheme, GraphCanvas, GraphEdge, GraphNode, lightTheme } from 'reagraph';
import Modal from '../../components/Common/Modal'; import Modal from '../../components/Common/Modal';
import PrettyJson from '../../components/Common/PrettyJSON'; import { useConceptTheme } from '../../context/ThemeContext';
import { resources } from '../../utils/constants';
import { SyntaxTree } from '../../utils/models'; import { SyntaxTree } from '../../utils/models';
import { getNodeLabel } from '../../utils/staticUI';
interface DlgShowASTProps { interface DlgShowASTProps {
hideWindow: () => void hideWindow: () => void
syntaxTree: SyntaxTree syntaxTree: SyntaxTree
expression: string
} }
function DlgShowAST({ hideWindow, syntaxTree }: DlgShowASTProps) { function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
const handleSubmit = () => { const { darkMode } = useConceptTheme();
function handleSubmit() {
// Do nothing // Do nothing
}
const nodes: GraphNode[] = useMemo(
() => syntaxTree.map(node => {
return {
id: String(node.uid),
label: getNodeLabel(node)
}; };
}), [syntaxTree]);
const edges: GraphEdge[] = useMemo(
() => {
const result: GraphEdge[] = [];
syntaxTree.forEach(node => {
if (node.parent != node.uid) {
result.push({
id: String(node.uid),
source: String(node.parent),
target: String(node.uid)
});
}
});
return result;
}, [syntaxTree]);
return ( return (
<Modal <Modal
title='Просмотр дерева разбора'
hideWindow={hideWindow} hideWindow={hideWindow}
onSubmit={handleSubmit} onSubmit={handleSubmit}
submitText='Закрыть' submitText='Закрыть'
canSubmit={true} canSubmit={true}
> >
<div className='max-w-[40rem] max-h-[30rem] overflow-auto'> <div className='flex flex-col items-start gap-2'>
<PrettyJson data={syntaxTree}/> <div className='w-full text-lg text-center'>{expression}</div>
<div className='flex-wrap w-full h-full overflow-auto'>
<div className='relative w-[1040px] h-[600px] 2xl:w-[1680px] 2xl:h-[600px]'>
<GraphCanvas
nodes={nodes}
edges={edges}
layoutType='hierarchicalTd'
labelFontUrl={resources.graph_font}
theme={darkMode ? darkTheme : lightTheme}
/>
</div>
</div>
</div> </div>
</Modal> </Modal>
); );

View File

@ -14,7 +14,7 @@ import ViewSideConstituents from './elements/ViewSideConstituents';
import { RSTabsList } from './RSTabs'; import { RSTabsList } from './RSTabs';
interface EditorConstituentaProps { interface EditorConstituentaProps {
onShowAST: (ast: SyntaxTree) => void onShowAST: (expression: string, ast: SyntaxTree) => void
onShowCreateCst: (selectedID: number | undefined, type: CstType | undefined) => void onShowCreateCst: (selectedID: number | undefined, type: CstType | undefined) => void
} }

View File

@ -23,7 +23,7 @@ interface EditorRSExpressionProps {
placeholder?: string placeholder?: string
value: string value: string
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
onShowAST: (ast: SyntaxTree) => void onShowAST: (expression: string, ast: SyntaxTree) => void
toggleEditMode: () => void toggleEditMode: () => void
setTypification: (typificaiton: string) => void setTypification: (typificaiton: string) => void
setValue: (expression: string) => void setValue: (expression: string) => void
@ -247,7 +247,7 @@ function EditorRSExpression({
{ !loading && parseData && { !loading && parseData &&
<ParsingResult <ParsingResult
data={parseData} data={parseData}
onShowAST={onShowAST} onShowAST={ast => onShowAST(value, ast)}
onShowError={onShowError} onShowError={onShowError}
/>} />}
</div>} </div>}

View File

@ -40,6 +40,7 @@ function RSTabs() {
const [showUpload, setShowUpload] = useState(false); const [showUpload, setShowUpload] = useState(false);
const [showClone, setShowClone] = useState(false); const [showClone, setShowClone] = useState(false);
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]); const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
const [expression, setExpression] = useState('');
const [showAST, setShowAST] = useState(false); const [showAST, setShowAST] = useState(false);
const [defaultType, setDefaultType] = useState<CstType | undefined>(undefined); const [defaultType, setDefaultType] = useState<CstType | undefined>(undefined);
@ -136,8 +137,9 @@ function RSTabs() {
}, [handleAddNew]); }, [handleAddNew]);
const onShowAST = useCallback( const onShowAST = useCallback(
(ast: SyntaxTree) => { (expression: string, ast: SyntaxTree) => {
setSyntaxTree(ast); setSyntaxTree(ast);
setExpression(expression);
setShowAST(true); setShowAST(true);
}, []); }, []);
@ -157,6 +159,7 @@ function RSTabs() {
{showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>} {showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>}
{showAST && {showAST &&
<DlgShowAST <DlgShowAST
expression={expression}
syntaxTree={syntaxTree} syntaxTree={syntaxTree}
hideWindow={() => { setShowAST(false); }} hideWindow={() => { setShowAST(false); }}
/>} />}

View File

@ -20,7 +20,7 @@ export const urls = {
}; };
export const resources = { export const resources = {
graph_font: 'https://ey2pz3.csb.app/NotoSansSC-Regular.ttf' graph_font: '/DejaVu.ttf'
} }
export const prefixes = { export const prefixes = {

View File

@ -57,7 +57,10 @@ export interface ISyntaxTreeNode {
typeID: TokenID typeID: TokenID
start: number start: number
finish: number finish: number
data: unknown data: {
dataType: string,
value: unknown
}
} }
export type SyntaxTree = ISyntaxTreeNode[] export type SyntaxTree = ISyntaxTreeNode[]

View File

@ -1,7 +1,7 @@
import { LayoutTypes } from 'reagraph'; import { LayoutTypes } from 'reagraph';
import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums'; import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums';
import { CstType, ExpressionStatus, type IConstituenta, IRSErrorDescription,type IRSForm, ParsingStatus, ValueClass } from './models'; import { CstType, ExpressionStatus, type IConstituenta, IRSErrorDescription,type IRSForm, ISyntaxTreeNode,ParsingStatus, ValueClass } from './models';
export interface IRSButtonData { export interface IRSButtonData {
text: string text: string
@ -450,3 +450,80 @@ export function getRSErrorMessage(error: IRSErrorDescription): string {
} }
return 'UNKNOWN ERROR'; return 'UNKNOWN ERROR';
} }
export function getNodeLabel(node: ISyntaxTreeNode): string {
switch(node.typeID) {
case TokenID.ID_LOCAL: return node.data.value as string;
case TokenID.ID_GLOBAL: return node.data.value as string;
case TokenID.ID_FUNCTION: return node.data.value as string;
case TokenID.ID_PREDICATE: return node.data.value as string;
case TokenID.ID_RADICAL: return node.data.value as string;
case TokenID.LIT_INTEGER: return String(node.data.value as number);
case TokenID.BIGPR: return 'Pr' + (node.data.value as string[]).toString();
case TokenID.SMALLPR: return 'pr' + (node.data.value as string[]).toString();
case TokenID.FILTER: return 'Fi' + (node.data.value as string[]).toString();
case TokenID.PLUS: return '+'
case TokenID.MINUS: return '-'
case TokenID.MULTIPLY: return '*'
case TokenID.GREATER: return '>'
case TokenID.LESSER: return '<'
case TokenID.NT_TUPLE: return 'TUPLE'
case TokenID.NT_ENUMERATION: return 'ENUM'
case TokenID.NT_ENUM_DECL: return 'ENUM_DECLARATION'
case TokenID.NT_TUPLE_DECL: return 'TUPLE_DECLARATION'
case TokenID.PUNC_DEFINE: return 'DEFINITION'
case TokenID.NT_ARG_DECL: return 'ARG'
case TokenID.NT_FUNC_CALL: return 'CALL'
case TokenID.NT_ARGUMENTS: return 'ARGS'
case TokenID.NT_FUNC_DEFINITION: return 'FUNCTION_DEFINITION'
case TokenID.NT_IMP_DECLARE: return 'IDECLARE'
case TokenID.NT_IMP_ASSIGN: return 'IASSIGN'
case TokenID.NT_IMP_LOGIC: return 'ICHECK'
case TokenID.NT_RECURSIVE_SHORT: return getRSButtonData(TokenID.NT_RECURSIVE_FULL).text;
case TokenID.BOOLEAN:
case TokenID.DECART:
case TokenID.FORALL:
case TokenID.EXISTS:
case TokenID.NOT:
case TokenID.AND:
case TokenID.OR:
case TokenID.IMPLICATION:
case TokenID.EQUIVALENT:
case TokenID.LIT_EMPTYSET:
case TokenID.LIT_INTSET:
case TokenID.EQUAL:
case TokenID.NOTEQUAL:
case TokenID.GREATER_OR_EQ:
case TokenID.LESSER_OR_EQ:
case TokenID.IN:
case TokenID.NOTIN:
case TokenID.SUBSET_OR_EQ:
case TokenID.SUBSET:
case TokenID.NOTSUBSET:
case TokenID.INTERSECTION:
case TokenID.UNION:
case TokenID.SET_MINUS:
case TokenID.SYMMINUS:
case TokenID.NT_DECLARATIVE_EXPR:
case TokenID.NT_IMPERATIVE_EXPR:
case TokenID.NT_RECURSIVE_FULL:
case TokenID.REDUCE:
case TokenID.CARD:
case TokenID.BOOL:
case TokenID.DEBOOL:
case TokenID.PUNC_ASSIGN:
case TokenID.PUNC_ITERATE:
return getRSButtonData(node.typeID).text;
}
// node
return 'UNKNOWN ' + String(node.typeID);
}