fix:修复前端bug (#83)
Co-authored-by: jackfiled <xcrenchangjun@outlook.com> Reviewed-on: PostGuard/Canon#83 Co-authored-by: ichirinko <1621543655@qq.com> Co-committed-by: ichirinko <1621543655@qq.com>
This commit is contained in:
parent
d90f6fde62
commit
20e82c6f4f
19
Canon.Core/Exceptions/CanonException.cs
Normal file
19
Canon.Core/Exceptions/CanonException.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
namespace Canon.Core.Exceptions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编译器中的统一异常基类
|
||||||
|
/// </summary>
|
||||||
|
public class CanonException : Exception
|
||||||
|
{
|
||||||
|
public CanonException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CanonException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CanonException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ namespace Canon.Core.Exceptions;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语法分析中引发的异常
|
/// 语法分析中引发的异常
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GrammarException : Exception
|
public class GrammarException : CanonException
|
||||||
{
|
{
|
||||||
public override string Message { get; }
|
public override string Message { get; }
|
||||||
|
|
||||||
|
|
|
@ -3,32 +3,32 @@ using Enums;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 词法分析中引发的异常
|
/// 词法分析中引发的异常
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LexemeException : Exception
|
public class LexemeException : CanonException
|
||||||
{
|
{
|
||||||
public LexemeErrorType ErrorType { get; }
|
public LexemeErrorType ErrorType { get; }
|
||||||
|
|
||||||
public uint Line { get; }
|
public uint Line { get; }
|
||||||
|
|
||||||
public uint CharPosition { get; }
|
public uint CharPosition { get; }
|
||||||
public LexemeException() { }
|
|
||||||
|
|
||||||
public LexemeException(string message) : base(message) { }
|
private readonly string _message;
|
||||||
|
|
||||||
public LexemeException(string message, Exception innerException) :
|
|
||||||
base(message, innerException) { }
|
|
||||||
|
|
||||||
/// <param name="errorType">错误类型</param>
|
/// <param name="errorType">错误类型</param>
|
||||||
/// <param name="line">单词的行号</param>
|
/// <param name="line">单词的行号</param>
|
||||||
/// <param name="charPosition">单词的列号</param>
|
/// <param name="charPosition">单词的列号</param>
|
||||||
/// <param name="message">错误信息</param>
|
/// <param name="message">错误信息</param>
|
||||||
public LexemeException(LexemeErrorType errorType, uint line, uint charPosition, string message) :
|
public LexemeException(LexemeErrorType errorType, uint line, uint charPosition, string message)
|
||||||
base("line:" + line + ", charPosition:" + charPosition + " :" + message)
|
|
||||||
{
|
{
|
||||||
ErrorType = errorType;
|
ErrorType = errorType;
|
||||||
Line = line;
|
Line = line;
|
||||||
CharPosition = charPosition;
|
CharPosition = charPosition;
|
||||||
|
_message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string Message => ToString();
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"LexemeException: ErrorType={ErrorType}, Line={Line}, CharPosition={CharPosition}, Message={Message}\n";
|
return $"LexemeException: ErrorType={ErrorType}, Line={Line}, CharPosition={CharPosition}, Message={_message}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Canon.Server.Entities;
|
||||||
using Canon.Server.Services;
|
using Canon.Server.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
|
||||||
namespace Canon.Server.Controllers;
|
namespace Canon.Server.Controllers;
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ public class CompilerController(CompileDbContext dbContext, CompilerService comp
|
||||||
public async Task<ActionResult<CompileResponse>> GetResponse(string compileId)
|
public async Task<ActionResult<CompileResponse>> GetResponse(string compileId)
|
||||||
{
|
{
|
||||||
CompileResult? result = await (from item in dbContext.CompileResults.AsNoTracking()
|
CompileResult? result = await (from item in dbContext.CompileResults.AsNoTracking()
|
||||||
where item.CompileId == compileId
|
where item.Id == new ObjectId(compileId)
|
||||||
select item).FirstOrDefaultAsync();
|
select item).FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (result is null)
|
if (result is null)
|
||||||
|
@ -59,7 +60,7 @@ public class CompilerController(CompileDbContext dbContext, CompilerService comp
|
||||||
public async Task<IActionResult> DeleteCompileResult(string compileId)
|
public async Task<IActionResult> DeleteCompileResult(string compileId)
|
||||||
{
|
{
|
||||||
CompileResult? result = await (from item in dbContext.CompileResults
|
CompileResult? result = await (from item in dbContext.CompileResults
|
||||||
where item.CompileId == compileId
|
where item.Id == new ObjectId(compileId)
|
||||||
select item).FirstOrDefaultAsync();
|
select item).FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (result is null)
|
if (result is null)
|
||||||
|
|
|
@ -8,6 +8,9 @@ public class CompileResponse
|
||||||
[Required]
|
[Required]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public bool Error { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string SourceCode { get; set; }
|
public string SourceCode { get; set; }
|
||||||
|
|
||||||
|
@ -35,7 +38,8 @@ public class CompileResponse
|
||||||
|
|
||||||
public CompileResponse(CompileResult result)
|
public CompileResponse(CompileResult result)
|
||||||
{
|
{
|
||||||
Id = result.CompileId;
|
Id = result.Id.ToString();
|
||||||
|
Error = result.Error;
|
||||||
SourceCode = result.SourceCode;
|
SourceCode = result.SourceCode;
|
||||||
CompiledCode = result.CompiledCode;
|
CompiledCode = result.CompiledCode;
|
||||||
ImageAddress = $"/api/file/{result.SytaxTreeImageFilename}";
|
ImageAddress = $"/api/file/{result.SytaxTreeImageFilename}";
|
||||||
|
|
|
@ -7,8 +7,7 @@ public class CompileResult
|
||||||
{
|
{
|
||||||
public ObjectId Id { get; set; }
|
public ObjectId Id { get; set; }
|
||||||
|
|
||||||
[MaxLength(40)]
|
public bool Error { get; set; }
|
||||||
public string CompileId { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public string SourceCode { get; set; } = string.Empty;
|
public string SourceCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ builder.Services.AddDbContext<CompileDbContext>(options =>
|
||||||
options.UseMongoDB(connectionString, "Canon");
|
options.UseMongoDB(connectionString, "Canon");
|
||||||
});
|
});
|
||||||
builder.Services.AddGridFs(connectionString, "Canon");
|
builder.Services.AddGridFs(connectionString, "Canon");
|
||||||
builder.Services.AddSingleton<ICompilerLogger, CompilerLogger>();
|
|
||||||
builder.Services.AddTransient<ILexer, Lexer>();
|
builder.Services.AddTransient<ILexer, Lexer>();
|
||||||
builder.Services.AddSingleton<IGrammarParser>(
|
builder.Services.AddSingleton<IGrammarParser>(
|
||||||
_ => GeneratedGrammarParser.Instance);
|
_ => GeneratedGrammarParser.Instance);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Canon.Core.Abstractions;
|
using Canon.Core.Abstractions;
|
||||||
|
using Canon.Core.Exceptions;
|
||||||
using Canon.Core.LexicalParser;
|
using Canon.Core.LexicalParser;
|
||||||
using Canon.Core.SemanticParser;
|
using Canon.Core.SemanticParser;
|
||||||
using Canon.Core.SyntaxNodes;
|
using Canon.Core.SyntaxNodes;
|
||||||
|
@ -6,6 +7,7 @@ using Canon.Server.DataTransferObjects;
|
||||||
using Canon.Server.Entities;
|
using Canon.Server.Entities;
|
||||||
using Canon.Server.Models;
|
using Canon.Server.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
|
||||||
namespace Canon.Server.Services;
|
namespace Canon.Server.Services;
|
||||||
|
|
||||||
|
@ -13,7 +15,6 @@ public class CompilerService(
|
||||||
ILexer lexer,
|
ILexer lexer,
|
||||||
IGrammarParser grammarParser,
|
IGrammarParser grammarParser,
|
||||||
SyntaxTreeTraveller traveller,
|
SyntaxTreeTraveller traveller,
|
||||||
ICompilerLogger compilerLogger,
|
|
||||||
CompileDbContext dbContext,
|
CompileDbContext dbContext,
|
||||||
GridFsService gridFsService,
|
GridFsService gridFsService,
|
||||||
SyntaxTreePresentationService syntaxTreePresentationService,
|
SyntaxTreePresentationService syntaxTreePresentationService,
|
||||||
|
@ -34,19 +35,44 @@ public class CompilerService(
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeReader reader = new(sourceCode);
|
CodeReader reader = new(sourceCode);
|
||||||
IEnumerable<SemanticToken> tokens = lexer.Tokenize(reader);
|
|
||||||
ProgramStruct root = grammarParser.Analyse(tokens);
|
ProgramStruct root;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IEnumerable<SemanticToken> tokens = lexer.Tokenize(reader);
|
||||||
|
root = grammarParser.Analyse(tokens);
|
||||||
|
}
|
||||||
|
catch (CanonException e)
|
||||||
|
{
|
||||||
|
CompileResult errorResult = new()
|
||||||
|
{
|
||||||
|
Id = ObjectId.GenerateNewId(),
|
||||||
|
Error = true,
|
||||||
|
SourceCode = sourceCode.Code,
|
||||||
|
CompiledCode = string.Empty,
|
||||||
|
SytaxTreeImageFilename = string.Empty,
|
||||||
|
CompileTime = DateTime.Now,
|
||||||
|
CompileInformation = e.Message
|
||||||
|
};
|
||||||
|
|
||||||
|
await dbContext.CompileResults.AddAsync(errorResult);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
return new CompileResponse(errorResult);
|
||||||
|
}
|
||||||
|
|
||||||
await using Stream imageStream = syntaxTreePresentationService.Present(root);
|
await using Stream imageStream = syntaxTreePresentationService.Present(root);
|
||||||
string filename = await gridFsService.UploadStream(imageStream, "image/png");
|
string filename = await gridFsService.UploadStream(imageStream, "image/png");
|
||||||
|
|
||||||
|
ICompilerLogger compilerLogger = new CompilerLogger();
|
||||||
CodeGeneratorVisitor visitor = new(compilerLogger);
|
CodeGeneratorVisitor visitor = new(compilerLogger);
|
||||||
traveller.Travel(root, visitor);
|
traveller.Travel(root, visitor);
|
||||||
|
|
||||||
CompileResult result = new()
|
CompileResult result = new()
|
||||||
{
|
{
|
||||||
|
Id = ObjectId.GenerateNewId(),
|
||||||
|
Error = visitor.IsError,
|
||||||
SourceCode = sourceCode.Code,
|
SourceCode = sourceCode.Code,
|
||||||
CompileId = Guid.NewGuid().ToString(),
|
|
||||||
CompiledCode = visitor.Builder.Build(),
|
CompiledCode = visitor.Builder.Build(),
|
||||||
SytaxTreeImageFilename = filename,
|
SytaxTreeImageFilename = filename,
|
||||||
CompileTime = DateTime.Now,
|
CompileTime = DateTime.Now,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import {AppBar, Button, Grid, Toolbar, Typography} from "@mui/material";
|
import { AppBar, Button, Grid, Toolbar, Typography } from "@mui/material";
|
||||||
import {InputField} from "./InputField.tsx";
|
import { InputField } from "./InputField.tsx";
|
||||||
import {CSSProperties, useEffect, useState} from "react";
|
import { CSSProperties, useEffect, useState } from "react";
|
||||||
import {OutputField} from "./OutputField.tsx";
|
import { OutputField } from "./OutputField.tsx";
|
||||||
import createClient from "openapi-fetch";
|
import createClient from "openapi-fetch";
|
||||||
import * as openapi from '../openapi';
|
import * as openapi from '../openapi';
|
||||||
import {enqueueSnackbar} from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import {useNavigate} from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {HistoryPage} from "./HistoryPage.tsx";
|
import { HistoryPage } from "./HistoryPage.tsx";
|
||||||
|
|
||||||
const client = createClient<openapi.paths>();
|
const client = createClient<openapi.paths>();
|
||||||
|
|
||||||
|
@ -16,13 +16,14 @@ export function Index() {
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [outputValue, setOutputValue] = useState<openapi.components["schemas"]["CompileResponse"]>({
|
const [outputValue, setOutputValue] = useState<openapi.components["schemas"]["CompileResponse"]>({
|
||||||
compiledCode: "",
|
compiledCode: "",
|
||||||
|
error: false,
|
||||||
sourceCode: "",
|
sourceCode: "",
|
||||||
id: "",
|
id: "",
|
||||||
imageAddress: "",
|
imageAddress: "",
|
||||||
compileTime: "",
|
compileTime: "",
|
||||||
compileInformation: ""
|
compileInformation: ""
|
||||||
});
|
});
|
||||||
const [historyPageState,setHistoryPageState] = useState(false);
|
const [historyPageState, setHistoryPageState] = useState(false);
|
||||||
const navigate = useNavigate(); // 跳转hook
|
const navigate = useNavigate(); // 跳转hook
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -32,6 +33,7 @@ export function Index() {
|
||||||
setInputValue("");
|
setInputValue("");
|
||||||
setOutputValue({
|
setOutputValue({
|
||||||
compiledCode: "",
|
compiledCode: "",
|
||||||
|
error: false,
|
||||||
sourceCode: "",
|
sourceCode: "",
|
||||||
id: "",
|
id: "",
|
||||||
imageAddress: "pic/uncompiled.png",
|
imageAddress: "pic/uncompiled.png",
|
||||||
|
@ -41,14 +43,14 @@ export function Index() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const getCompileInstance = async () => {
|
const getCompileInstance = async () => {
|
||||||
const {data} = await client.GET("/api/Compiler/{compileId}", {
|
const { data } = await client.GET("/api/Compiler/{compileId}", {
|
||||||
params:
|
params:
|
||||||
|
{
|
||||||
|
path:
|
||||||
{
|
{
|
||||||
path:
|
compileId: path
|
||||||
{
|
|
||||||
compileId: path
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (data !== undefined) {
|
if (data !== undefined) {
|
||||||
setInputValue(data.sourceCode);
|
setInputValue(data.sourceCode);
|
||||||
|
@ -66,21 +68,24 @@ export function Index() {
|
||||||
|
|
||||||
async function compilerButtonClick() {
|
async function compilerButtonClick() {
|
||||||
|
|
||||||
const {data} = await client.POST("/api/Compiler", {
|
const { data } = await client.POST("/api/Compiler", {
|
||||||
body: {
|
body: {
|
||||||
code: inputValue
|
code: inputValue
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
if (data !== undefined) {
|
if (data == undefined) {
|
||||||
setOutputValue(data);
|
enqueueSnackbar("内部错误", { variant: "error", anchorOrigin: { vertical: 'bottom', horizontal: 'right' } });
|
||||||
enqueueSnackbar("编译成功", {variant: "success", anchorOrigin: {vertical: 'bottom', horizontal: 'right'}});
|
return;
|
||||||
navigate(`/${data.id}`, {})
|
}
|
||||||
|
|
||||||
|
if (!data.error) {
|
||||||
|
enqueueSnackbar("编译成功", { variant: "success", anchorOrigin: { vertical: 'bottom', horizontal: 'right' } });
|
||||||
} else {
|
} else {
|
||||||
// error
|
// error
|
||||||
enqueueSnackbar("编译失败", {variant: "error", anchorOrigin: {vertical: 'bottom', horizontal: 'right'}});
|
enqueueSnackbar("编译失败", { variant: "error", anchorOrigin: { vertical: 'bottom', horizontal: 'right' } });
|
||||||
}
|
}
|
||||||
|
navigate(`/${data.id}`, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function historyButtonClick() {
|
function historyButtonClick() {
|
||||||
|
@ -89,9 +94,9 @@ export function Index() {
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className={"title"}
|
<div className={"title"}
|
||||||
style={titleClassCss}>
|
style={titleClassCss}>
|
||||||
<AppBar style={{zIndex: 0}}>
|
<AppBar style={{ zIndex: 0 }}>
|
||||||
<Toolbar style={{width: '100%'}}>
|
<Toolbar style={{ width: '100%' }}>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
Canon
|
Canon
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -101,9 +106,9 @@ export function Index() {
|
||||||
transform: "translateX(-50%)",
|
transform: "translateX(-50%)",
|
||||||
fontSize: "medium",
|
fontSize: "medium",
|
||||||
}}
|
}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
onClick={compilerButtonClick}
|
onClick={compilerButtonClick}
|
||||||
>
|
>
|
||||||
编译
|
编译
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -113,29 +118,29 @@ export function Index() {
|
||||||
right: "10%",
|
right: "10%",
|
||||||
fontSize: "medium",
|
fontSize: "medium",
|
||||||
}}
|
}}
|
||||||
variant="text"
|
variant="text"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
onClick={historyButtonClick}>
|
onClick={historyButtonClick}>
|
||||||
历史记录
|
历史记录
|
||||||
</Button>
|
</Button>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={"content"}
|
<div className={"content"}
|
||||||
style={contentClassCss}>
|
style={contentClassCss}>
|
||||||
<Grid container spacing={2} style={{width: "100%", height: "100%"}}>
|
<Grid container spacing={2} style={{ width: "100%", height: "100%" }}>
|
||||||
<Grid item xs={12} sm={6} style={{width: "100%", height: "100%"}}>
|
<Grid item xs={12} sm={6} style={{ width: "100%", height: "100%" }}>
|
||||||
<InputField defaultValue={inputValue} onValueChange={handleValueChange}>
|
<InputField defaultValue={inputValue} onValueChange={handleValueChange}>
|
||||||
</InputField>
|
</InputField>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6} style={{width: "100%", height: "100%"}}>
|
<Grid item xs={12} sm={6} style={{ width: "100%", height: "100%" }}>
|
||||||
<OutputField data={outputValue}>
|
<OutputField data={outputValue}>
|
||||||
</OutputField>
|
</OutputField>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
<HistoryPage state = {historyPageState} setState={setHistoryPageState}>
|
<HistoryPage state={historyPageState} setState={setHistoryPageState}>
|
||||||
|
|
||||||
</HistoryPage>
|
</HistoryPage>
|
||||||
</>
|
</>
|
||||||
|
|
1
Canon.Server/client-app/src/openapi.d.ts
vendored
1
Canon.Server/client-app/src/openapi.d.ts
vendored
|
@ -115,6 +115,7 @@ export interface components {
|
||||||
schemas: {
|
schemas: {
|
||||||
CompileResponse: {
|
CompileResponse: {
|
||||||
id: string;
|
id: string;
|
||||||
|
error: boolean;
|
||||||
sourceCode: string;
|
sourceCode: string;
|
||||||
compiledCode: string;
|
compiledCode: string;
|
||||||
imageAddress: string;
|
imageAddress: string;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user