mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
Implement SyntaxTree visualization
This commit is contained in:
parent
a8bbb2b63c
commit
c7d4902137
BIN
rsconcept/frontend/public/DejaVu.ttf
Normal file
BIN
rsconcept/frontend/public/DejaVu.ttf
Normal file
Binary file not shown.
|
@ -33,7 +33,7 @@ function Modal({ title, hideWindow, onSubmit, onCancel, canSubmit, children, sub
|
|||
</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]'>
|
||||
{ title && <h1 className='mb-2 text-xl font-bold text-center'>{title}</h1> }
|
||||
<div className='py-2'>
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
<div className='flex justify-between w-full pt-4 mt-2 border-t-4'>
|
||||
|
|
|
@ -36,6 +36,7 @@ function DlgCreateCst({ hideWindow, defaultType, onCreate }: DlgCreateCstProps)
|
|||
onSubmit={handleSubmit}
|
||||
>
|
||||
<ConceptSelect
|
||||
className='my-4'
|
||||
options={CstTypeSelector}
|
||||
placeholder='Выберите тип'
|
||||
values={selectedType ? [{ value: selectedType, label: getCstTypeLabel(selectedType) }] : []}
|
||||
|
|
|
@ -1,27 +1,68 @@
|
|||
import { useMemo } from 'react';
|
||||
import { darkTheme, GraphCanvas, GraphEdge, GraphNode, lightTheme } from 'reagraph';
|
||||
|
||||
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 { getNodeLabel } from '../../utils/staticUI';
|
||||
|
||||
interface DlgShowASTProps {
|
||||
hideWindow: () => void
|
||||
syntaxTree: SyntaxTree
|
||||
expression: string
|
||||
}
|
||||
|
||||
function DlgShowAST({ hideWindow, syntaxTree }: DlgShowASTProps) {
|
||||
const handleSubmit = () => {
|
||||
function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||
const { darkMode } = useConceptTheme();
|
||||
|
||||
function handleSubmit() {
|
||||
// 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 (
|
||||
<Modal
|
||||
title='Просмотр дерева разбора'
|
||||
hideWindow={hideWindow}
|
||||
onSubmit={handleSubmit}
|
||||
submitText='Закрыть'
|
||||
canSubmit={true}
|
||||
>
|
||||
<div className='max-w-[40rem] max-h-[30rem] overflow-auto'>
|
||||
<PrettyJson data={syntaxTree}/>
|
||||
<div className='flex flex-col items-start gap-2'>
|
||||
<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>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -14,7 +14,7 @@ import ViewSideConstituents from './elements/ViewSideConstituents';
|
|||
import { RSTabsList } from './RSTabs';
|
||||
|
||||
interface EditorConstituentaProps {
|
||||
onShowAST: (ast: SyntaxTree) => void
|
||||
onShowAST: (expression: string, ast: SyntaxTree) => void
|
||||
onShowCreateCst: (selectedID: number | undefined, type: CstType | undefined) => void
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ interface EditorRSExpressionProps {
|
|||
placeholder?: string
|
||||
value: string
|
||||
onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
|
||||
onShowAST: (ast: SyntaxTree) => void
|
||||
onShowAST: (expression: string, ast: SyntaxTree) => void
|
||||
toggleEditMode: () => void
|
||||
setTypification: (typificaiton: string) => void
|
||||
setValue: (expression: string) => void
|
||||
|
@ -247,7 +247,7 @@ function EditorRSExpression({
|
|||
{ !loading && parseData &&
|
||||
<ParsingResult
|
||||
data={parseData}
|
||||
onShowAST={onShowAST}
|
||||
onShowAST={ast => onShowAST(value, ast)}
|
||||
onShowError={onShowError}
|
||||
/>}
|
||||
</div>}
|
||||
|
|
|
@ -40,6 +40,7 @@ function RSTabs() {
|
|||
const [showUpload, setShowUpload] = useState(false);
|
||||
const [showClone, setShowClone] = useState(false);
|
||||
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
|
||||
const [expression, setExpression] = useState('');
|
||||
const [showAST, setShowAST] = useState(false);
|
||||
|
||||
const [defaultType, setDefaultType] = useState<CstType | undefined>(undefined);
|
||||
|
@ -136,8 +137,9 @@ function RSTabs() {
|
|||
}, [handleAddNew]);
|
||||
|
||||
const onShowAST = useCallback(
|
||||
(ast: SyntaxTree) => {
|
||||
(expression: string, ast: SyntaxTree) => {
|
||||
setSyntaxTree(ast);
|
||||
setExpression(expression);
|
||||
setShowAST(true);
|
||||
}, []);
|
||||
|
||||
|
@ -157,6 +159,7 @@ function RSTabs() {
|
|||
{showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>}
|
||||
{showAST &&
|
||||
<DlgShowAST
|
||||
expression={expression}
|
||||
syntaxTree={syntaxTree}
|
||||
hideWindow={() => { setShowAST(false); }}
|
||||
/>}
|
||||
|
|
|
@ -20,7 +20,7 @@ export const urls = {
|
|||
};
|
||||
|
||||
export const resources = {
|
||||
graph_font: 'https://ey2pz3.csb.app/NotoSansSC-Regular.ttf'
|
||||
graph_font: '/DejaVu.ttf'
|
||||
}
|
||||
|
||||
export const prefixes = {
|
||||
|
|
|
@ -57,7 +57,10 @@ export interface ISyntaxTreeNode {
|
|||
typeID: TokenID
|
||||
start: number
|
||||
finish: number
|
||||
data: unknown
|
||||
data: {
|
||||
dataType: string,
|
||||
value: unknown
|
||||
}
|
||||
}
|
||||
export type SyntaxTree = ISyntaxTreeNode[]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { LayoutTypes } from 'reagraph';
|
||||
|
||||
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 {
|
||||
text: string
|
||||
|
@ -450,3 +450,80 @@ export function getRSErrorMessage(error: IRSErrorDescription): string {
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user