mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50: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>
|
||||||
<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'>
|
||||||
|
|
|
@ -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) }] : []}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>}
|
||||||
|
|
|
@ -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); }}
|
||||||
/>}
|
/>}
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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[]
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user