diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e1c41380a..cb023420a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - name: Install run: pnpm install - - run: npm run build:editor + - run: npm run build codecov: runs-on: macos-latest @@ -66,7 +66,7 @@ jobs: - name: Install run: pnpm install - name: Build - run: npm run build:editor + run: npm run build - name: Test run: npm run test-cov - run: pnpm install codecov -w @@ -94,7 +94,7 @@ jobs: - name: Run Cypress Tests uses: cypress-io/github-action@v5 with: - build: npm run build:editor + build: npm run build start: npm run e2e:case wait-on: "http://localhost:5175" wait-on-timeout: 120 diff --git a/package.json b/package.json index f4d6d6cf75..e28c8227a6 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,14 @@ "test-debug": "cross-env TS_NODE_PROJECT=tsconfig.tests.json floss --path tests -r ts-node/register --debug", "test-cov": "cross-env TS_NODE_PROJECT=tsconfig.tests.json IS_COV=1 nyc --reporter=lcov floss --path tests -r ts-node/register", "build": "npm run b:module && npm run b:types", - "build:editor": "npm run b:module:editor && npm run b:types", "lint": "eslint packages/*/src --ext .ts", "watch": "cross-env NODE_ENV=release BUILD_TYPE=MODULE rollup -cw -m inline", - "watch:editor": "cross-env NODE_ENV=editor BUILD_TYPE=MODULE rollup -cw -m inline", "watch:umd": "cross-env NODE_ENV=release BUILD_TYPE=UMD rollup -cw -m inline", - "watch:umd:editor": "cross-env NODE_ENV=editor BUILD_TYPE=UMD rollup -cw -m inline", "b:types": "pnpm -r --filter=./packages/* run b:types", "b:module": "cross-env BUILD_TYPE=MODULE NODE_ENV=release rollup -c", - "b:module:editor": "cross-env BUILD_TYPE=MODULE NODE_ENV=editor rollup -c", "b:umd": "cross-env BUILD_TYPE=UMD NODE_ENV=release rollup -c", "b:miniprogram": "cross-env BUILD_TYPE=MINI rollup -c", "b:all": "cross-env NODE_ENV=release npm run b:types && cross-env BUILD_TYPE=ALL NODE_ENV=release rollup -c", - "b:all:editor": "cross-env NODE_ENV=editor npm run b:types && cross-env BUILD_TYPE=ALL rollup -c", "clean": "pnpm -r exec rm -rf dist && pnpm -r exec rm -rf types", "e2e:case": "pnpm -C ./e2e run case", "e2e": "cypress run --browser chrome --headless", diff --git a/packages/core/src/shader/ShaderPass.ts b/packages/core/src/shader/ShaderPass.ts index 934b8030a0..99b0491336 100644 --- a/packages/core/src/shader/ShaderPass.ts +++ b/packages/core/src/shader/ShaderPass.ts @@ -180,7 +180,7 @@ export class ShaderPass extends ShaderPart { } const start = performance.now(); - const { vertex, fragment } = Shader._shaderLab._parseShaderPass( + const shaderProgramSource = Shader._shaderLab._parseShaderPass( this._shaderLabSource, vertexEntry, fragmentEntry, @@ -191,7 +191,11 @@ export class ShaderPass extends ShaderPart { ); Logger.info(`[ShaderLab compilation] cost time: ${performance.now() - start}ms`); - return new ShaderProgram(engine, vertex, fragment); + if (shaderProgramSource) { + return new ShaderProgram(engine, shaderProgramSource.vertex, shaderProgramSource.fragment); + } else { + return new ShaderProgram(engine, "", ""); + } } // TODO: remove it after migrate all shader to `ShaderLab`. diff --git a/packages/shader-lab/README.md b/packages/shader-lab/README.md index 6a6b323460..92a1c6bd53 100644 --- a/packages/shader-lab/README.md +++ b/packages/shader-lab/README.md @@ -26,6 +26,14 @@ const shader = Shader.create(galaceanShaderCode); engine.run() ``` +There are two versions of ShaderLab: `Release` and `Verbose`. The `Verbose` version offers more user-friendly diagnostic information for debug ShaderLab compilation errors, while the Release version provides superior performance. + +you can use `Verbose` version by import: + +```ts +import { ShaderLab } from "@galacean/engine-shader-lab/verbose"; +``` + ## CFG Grammar conflict detection The Galacean ShaderLab syntax is defined using Context-Free Grammar (CFG) and is documented within the `\*.y` file. When modifications to the ShaderLab syntax are required, it is recommended to make changes to the existing CFG syntax file, and employ [Bison](https://www.gnu.org/software/bison/manual/bison.html) to detect any potential grammar conflicts. diff --git a/packages/shader-lab/package.json b/packages/shader-lab/package.json index 9ff3d0a370..a4297bb52d 100644 --- a/packages/shader-lab/package.json +++ b/packages/shader-lab/package.json @@ -25,7 +25,8 @@ }, "files": [ "dist/**/*", - "types/**/*" + "types/**/*", + "verbose/package.json" ], "devDependencies": { "@galacean/engine-design": "workspace:*", diff --git a/packages/shader-lab/src/Error.ts b/packages/shader-lab/src/Error.ts deleted file mode 100644 index 6649f64157..0000000000 --- a/packages/shader-lab/src/Error.ts +++ /dev/null @@ -1,19 +0,0 @@ -// #if _EDITOR -import { ShaderRange } from "./common"; - -export abstract class GSError extends Error { - readonly loc: ShaderRange; - - constructor(message: string, loc: ShaderRange, cause?: Error) { - super(message, { cause }); - this.loc = loc; - } -} - -export class SemanticError extends GSError { - constructor(message: string, loc: ShaderRange, cause?: Error) { - super(message, loc, cause); - this.name = "SemanticError"; - } -} -// #endif diff --git a/packages/shader-lab/src/GSError.ts b/packages/shader-lab/src/GSError.ts new file mode 100644 index 0000000000..cbce38d1f4 --- /dev/null +++ b/packages/shader-lab/src/GSError.ts @@ -0,0 +1,67 @@ +// #if _VERBOSE +import { ShaderPosition } from "./common/ShaderPosition"; +import { ShaderRange } from "./common/ShaderRange"; + +export class GSError extends Error { + static wrappingLineCount = 2; + + constructor( + name: GSErrorName, + message: string, + public readonly location: ShaderRange | ShaderPosition, + public readonly source: string, + public readonly file?: string + ) { + super(message); + this.name = name; + } + + override toString(): string { + let start: ShaderPosition, end: ShaderPosition; + const { message, location, source } = this; + if (!source) { + return message; + } + + if (location instanceof ShaderPosition) { + start = end = location; + } else { + start = location.start; + end = location.end; + } + const lines = source.split("\n"); + + let diagnosticMessage = `${this.name}: ${message}\n\n`; + const lineSplit = "|ยทยทยท"; + + const wrappingLineCount = GSError.wrappingLineCount; + for (let i = start.line - wrappingLineCount, n = end.line + wrappingLineCount; i <= n; i++) { + const line = lines[i]; + diagnosticMessage += lineSplit + `${line}\n`; + + if (i < start.line || i > end.line) continue; + + let remarkStart = 0; + let remarkEnd = line.length; + let paddingLength = lineSplit.length; + if (i === start.line) { + remarkStart = start.column; + paddingLength += start.column; + } else if (i === end.line) { + remarkEnd = end.column; + } + const remarkLength = Math.max(remarkEnd - remarkStart, 1); + + diagnosticMessage += " ".repeat(paddingLength) + "^".repeat(remarkLength) + "\n"; + } + + return diagnosticMessage; + } +} + +// #endif +export enum GSErrorName { + PreprocessorError = "PreprocessorError", + CompilationError = "CompilationError", + ScannerError = "ScannerError" +} diff --git a/packages/shader-lab/src/Utils.ts b/packages/shader-lab/src/ParserUtils.ts similarity index 82% rename from packages/shader-lab/src/Utils.ts rename to packages/shader-lab/src/ParserUtils.ts index 38614c1e0e..a269ef3199 100644 --- a/packages/shader-lab/src/Utils.ts +++ b/packages/shader-lab/src/ParserUtils.ts @@ -1,11 +1,10 @@ import { ENonTerminal, GrammarSymbol } from "./parser/GrammarSymbol"; import { BaseToken as Token } from "./common/BaseToken"; -import { EKeyword, ETokenType, GalaceanDataType, ShaderRange, ShaderPosition } from "./common"; +import { EKeyword, ETokenType, GalaceanDataType } from "./common"; import { TreeNode } from "./parser/AST"; -// #if _EDITOR +// #if _VERBOSE import State from "./lalr/State"; // #endif -import { Logger } from "@galacean/engine"; export class ParserUtils { static unwrapNodeByType(node: TreeNode, type: ENonTerminal): T | undefined { @@ -15,7 +14,7 @@ export class ParserUtils { return ParserUtils.unwrapNodeByType(child, type); } - // #if _EDITOR + // #if _VERBOSE /** * Check if type `tb` is compatible with type `ta`. */ @@ -39,15 +38,10 @@ export class ParserUtils { return sm < ENonTerminal.START; } - static throw(pos: ShaderPosition | ShaderRange | number, ...msgs: any[]) { - Logger.error(pos.toString(), ...msgs); - throw msgs.join(" "); - } - /** * @internal */ - // #if _EDITOR + // #if _VERBOSE static printStatePool(logPath: string) { let output = ""; diff --git a/packages/shader-lab/src/ShaderLab.ts b/packages/shader-lab/src/ShaderLab.ts index 335d64c47e..37df11060b 100644 --- a/packages/shader-lab/src/ShaderLab.ts +++ b/packages/shader-lab/src/ShaderLab.ts @@ -2,38 +2,33 @@ import { Lexer } from "./lexer"; import { ShaderTargetParser } from "./parser"; import { Preprocessor } from "./preprocessor"; import { GLES100Visitor, GLES300Visitor } from "./codeGen"; -import { IShaderContent, IShaderLab } from "@galacean/engine-design/src/shader-lab"; +import { IShaderContent, IShaderLab } from "@galacean/engine-design"; import { ShaderContentParser } from "./contentParser"; // @ts-ignore -import { Logger, ShaderLib, ShaderMacro, ShaderPass, ShaderPlatformTarget } from "@galacean/engine"; +import { Logger, ShaderLib, ShaderMacro, ShaderPlatformTarget } from "@galacean/engine"; import { ShaderPosition, ShaderRange } from "./common"; -import { ShaderLabObjectPool } from "./ShaderLabObjectPool"; +// #if _VERBOSE +import { GSError } from "./GSError"; +// #endif +import { PpParser } from "./preprocessor/PpParser"; +import { ShaderLabUtils } from "./ShaderLabUtils"; +import { IShaderProgramSource } from "@galacean/engine-design/types/shader-lab/IShaderProgramSource"; +/** @internal */ export class ShaderLab implements IShaderLab { - /** - * @internal - */ private static _parser = ShaderTargetParser.create(); - /** - * @internal - */ - private static _shaderPositionPool = new ShaderLabObjectPool(ShaderPosition); - /** - * @internal - */ - private static _shaderRangePool = new ShaderLabObjectPool(ShaderRange); + private static _shaderPositionPool = ShaderLabUtils.createObjectPool(ShaderPosition); + private static _shaderRangePool = ShaderLabUtils.createObjectPool(ShaderRange); - static createPosition( - index: number, - // #if _EDITOR - line?: number, - column?: number - // #endif - ): ShaderPosition { + // #if _VERBOSE + static _processingPassText?: string; + // #endif + + static createPosition(index: number, line?: number, column?: number): ShaderPosition { const position = this._shaderPositionPool.get(); - position.setX( + position.set( index, - // #if _EDITOR + // #if _VERBOSE line, column // #endif @@ -43,10 +38,15 @@ export class ShaderLab implements IShaderLab { static createRange(start: ShaderPosition, end: ShaderPosition): ShaderRange { const range = this._shaderRangePool.get(); - range.setX(start, end); + range.set(start, end); return range; } + // #if _VERBOSE + /** Retrieve the compilation errors */ + readonly errors: GSError[] = []; + // #endif + _parseShaderPass( source: string, vertexEntry: string, @@ -55,8 +55,7 @@ export class ShaderLab implements IShaderLab { backend: ShaderPlatformTarget, platformMacros: string[], basePathForIncludeKey: string - ) { - ShaderLabObjectPool.clearAllShaderLabObjectPool(); + ): IShaderProgramSource | undefined { Preprocessor.reset(ShaderLib, basePathForIncludeKey); for (const macro of macros) { Preprocessor.addPredefinedMacro(macro.name, macro.value); @@ -66,68 +65,82 @@ export class ShaderLab implements IShaderLab { Preprocessor.addPredefinedMacro(platformMacros[i]); } - // #if _EDITOR - // TODO: index to position - // Logger.convertSourceIndex = Preprocessor.convertSourceIndex.bind(Preprocessor); - // #endif - const preprocessorStart = performance.now(); - const ppdContent = Preprocessor.process(source); + // #if _VERBOSE + if (PpParser._errors.length > 0) { + for (const err of PpParser._errors) { + this.errors.push(err); + } + this._logErrors(); + return undefined; + } + // #endif Logger.info(`[pass compilation - preprocessor] cost time ${performance.now() - preprocessorStart}ms`); const lexer = new Lexer(ppdContent); const tokens = lexer.tokenize(); - const program = ShaderLab._parser.parse(tokens); + + const { _parser: parser } = ShaderLab; + + ShaderLab._processingPassText = ppdContent; + const program = parser.parse(tokens); + + // #if _VERBOSE + for (const err of parser.errors) { + this.errors.push(err); + } if (!program) { - return { vertex: "", fragment: "" }; + this._logErrors(); + return undefined; } + // #endif + const codeGen = backend === ShaderPlatformTarget.GLES100 ? GLES100Visitor.getVisitor() : GLES300Visitor.getVisitor(); const start = performance.now(); const ret = codeGen.visitShaderProgram(program, vertexEntry, fragmentEntry); Logger.info(`[CodeGen] cost time: ${performance.now() - start}ms`); + ShaderLab._processingPassText = undefined; + + // #if _VERBOSE + for (const err of codeGen.errors) { + this.errors.push(err); + } + this._logErrors(); + // #endif return ret; } _parseShaderContent(shaderSource: string): IShaderContent { + ShaderLabUtils.clearAllShaderLabObjectPool(); ShaderContentParser.reset(); - return ShaderContentParser.parse(shaderSource); + const ret = ShaderContentParser.parse(shaderSource); + + // #if _VERBOSE + this.errors.length = 0; + for (const error of ShaderContentParser._errors) { + this.errors.push(error); + } + // #endif + + return ret; } - // #if _EDITOR + // #if _VERBOSE /** * @internal - * For debug */ - _parse( - shaderSource: string, - macros: ShaderMacro[], - backend: ShaderPlatformTarget - ): (ReturnType & { name: string })[] { - const structInfo = this._parseShaderContent(shaderSource); - const passResult = [] as any; - for (const subShader of structInfo.subShaders) { - for (const pass of subShader.passes) { - if (pass.isUsePass) continue; - const passInfo = this._parseShaderPass( - pass.contents, - pass.vertexEntry, - pass.fragmentEntry, - macros, - backend, - [], - // @ts-ignore - new URL(pass.name, ShaderPass._shaderRootPath).href - ) as any; - passInfo.name = pass.name; - passResult.push(passInfo); - } + _logErrors() { + const errors = this.errors; + if (errors.length === 0 || !Logger.isEnabled) return; + Logger.error(`${errors.length} errors occur!`); + for (const err of errors) { + Logger.error(err.toString()); } - return passResult; } // #endif } diff --git a/packages/shader-lab/src/ShaderLabObjectPool.ts b/packages/shader-lab/src/ShaderLabObjectPool.ts deleted file mode 100644 index 6284dc1593..0000000000 --- a/packages/shader-lab/src/ShaderLabObjectPool.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ClearableObjectPool, IPoolElement } from "@galacean/engine"; - -/** - * @internal - */ -export class ShaderLabObjectPool extends ClearableObjectPool { - static ShaderLabObjectPoolSet: ShaderLabObjectPool[] = []; - static clearAllShaderLabObjectPool() { - for (let i = 0; i < this.ShaderLabObjectPoolSet.length; i++) { - this.ShaderLabObjectPoolSet[i].clear(); - } - } - - constructor(type: new () => T) { - super(type); - ShaderLabObjectPool.ShaderLabObjectPoolSet.push(this); - } -} diff --git a/packages/shader-lab/src/ShaderLabUtils.ts b/packages/shader-lab/src/ShaderLabUtils.ts new file mode 100644 index 0000000000..b2847b67e8 --- /dev/null +++ b/packages/shader-lab/src/ShaderLabUtils.ts @@ -0,0 +1,37 @@ +import { ClearableObjectPool, IPoolElement } from "@galacean/engine"; +import { GSErrorName } from "./GSError"; +import { ShaderRange } from "./common/ShaderRange"; +import { ShaderPosition } from "./common/ShaderPosition"; +// #if _VERBOSE +import { GSError } from "./GSError"; +// #endif + +export class ShaderLabUtils { + private static _shaderLabObjectPoolSet: ClearableObjectPool[] = []; + + static createObjectPool(type: new () => T) { + const pool = new ClearableObjectPool(type); + ShaderLabUtils._shaderLabObjectPoolSet.push(pool); + return pool; + } + + static clearAllShaderLabObjectPool() { + for (let i = 0, n = ShaderLabUtils._shaderLabObjectPoolSet.length; i < n; i++) { + ShaderLabUtils._shaderLabObjectPoolSet[i].clear(); + } + } + + static createGSError( + message: string, + errorName: GSErrorName, + source: string, + location: ShaderRange | ShaderPosition, + file?: string + ) { + // #if _VERBOSE + return new GSError(errorName, message, location, source, file); + // #else + throw new Error(`[${errorName}]: ${message}`); + // #endif + } +} diff --git a/packages/shader-lab/src/codeGen/CodeGenVisitor.ts b/packages/shader-lab/src/codeGen/CodeGenVisitor.ts index ba7c2f5780..a42b3abe33 100644 --- a/packages/shader-lab/src/codeGen/CodeGenVisitor.ts +++ b/packages/shader-lab/src/codeGen/CodeGenVisitor.ts @@ -1,17 +1,25 @@ import { ENonTerminal } from "../parser/GrammarSymbol"; import { BaseToken as Token } from "../common/BaseToken"; -import { EKeyword } from "../common"; +import { EKeyword, ShaderPosition, ShaderRange } from "../common"; import { ASTNode, TreeNode } from "../parser/AST"; import { ESymbolType, FnSymbol, VarSymbol } from "../parser/symbolTable"; -import { ParserUtils } from "../Utils"; +import { ParserUtils } from "../ParserUtils"; import { NodeChild } from "../parser/types"; import { VisitorContext } from "./VisitorContext"; +import { ShaderLab } from "../ShaderLab"; +import { GSErrorName } from "../GSError"; +// #if _VERBOSE +import { GSError } from "../GSError"; +// #endif /** + * @internal * The code generator */ export class CodeGenVisitor { - protected constructor() {} + // #if _VERBOSE + readonly errors: GSError[] = []; + // #endif defaultCodeGen(children: NodeChild[]) { let ret: string[] = []; @@ -35,10 +43,20 @@ export class CodeGenVisitor { if (prop instanceof Token) { if (context.isAttributeStruct(postExpr.type)) { - context.referenceAttribute(prop.lexeme); + const error = context.referenceAttribute(prop); + // #if _VERBOSE + if (error) { + this.errors.push(error); + } + // #endif return prop.lexeme; } else if (context.isVaryingStruct(postExpr.type)) { - context.referenceVarying(prop.lexeme); + const error = context.referenceVarying(prop); + // #if _VERBOSE + if (error) { + this.errors.push(error); + } + // #endif return prop.lexeme; } @@ -172,4 +190,12 @@ export class CodeGenVisitor { visitFunctionIdentifier(node: ASTNode.FunctionIdentifier): string { return this.defaultCodeGen(node.children); } + + protected _reportError(loc: ShaderRange | ShaderPosition, message: string): void { + // #if _VERBOSE + this.errors.push(new GSError(GSErrorName.CompilationError, message, loc, ShaderLab._processingPassText)); + // #else + throw new Error(message); + // #endif + } } diff --git a/packages/shader-lab/src/codeGen/GLES100.ts b/packages/shader-lab/src/codeGen/GLES100.ts index e590e63c9f..fdfd225f71 100644 --- a/packages/shader-lab/src/codeGen/GLES100.ts +++ b/packages/shader-lab/src/codeGen/GLES100.ts @@ -1,4 +1,3 @@ -import { CodeGenVisitor } from "./CodeGenVisitor"; import { GLESVisitor } from "./GLESVisitor"; import { VisitorContext } from "./VisitorContext"; import { ICodeSegment } from "./types"; diff --git a/packages/shader-lab/src/codeGen/GLESVisitor.ts b/packages/shader-lab/src/codeGen/GLESVisitor.ts index 2f8e35cbab..e3923dcf3e 100644 --- a/packages/shader-lab/src/codeGen/GLESVisitor.ts +++ b/packages/shader-lab/src/codeGen/GLESVisitor.ts @@ -5,8 +5,8 @@ import { ESymbolType, FnSymbol, StructSymbol, SymbolInfo } from "../parser/symbo import { EShaderStage } from "../common/Enums"; import { IShaderInfo } from "@galacean/engine-design"; import { ICodeSegment } from "./types"; -import { Logger } from "@galacean/engine"; import { VisitorContext } from "./VisitorContext"; +import { EKeyword } from "../common"; const defaultPrecision = ` #ifdef GL_FRAGMENT_PRECISION_HIGH @@ -18,6 +18,9 @@ const defaultPrecision = ` #endif `; +/** + * @internal + */ export abstract class GLESVisitor extends CodeGenVisitor { protected _versionText: string = ""; protected _extensions: string = ""; @@ -26,6 +29,9 @@ export abstract class GLESVisitor extends CodeGenVisitor { abstract getVaryingDeclare(): ICodeSegment[]; visitShaderProgram(node: ASTNode.GLShaderProgram, vertexEntry: string, fragmentEntry: string): IShaderInfo { + // #if _VERBOSE + this.errors.length = 0; + // #endif VisitorContext.reset(); VisitorContext.context._passSymbolTable = node.shaderData.symbolTable; @@ -44,15 +50,15 @@ export abstract class GLESVisitor extends CodeGenVisitor { VisitorContext.context.stage = EShaderStage.VERTEX; const returnType = fnNode.protoType.returnType; - if (typeof returnType.type !== "string") { - Logger.warn("main entry can only return struct."); - } else { + if (typeof returnType.type === "string") { const varyStruct = symbolTable.lookup({ ident: returnType.type, symbolType: ESymbolType.STRUCT }); if (!varyStruct) { - Logger.warn("invalid varying struct:", returnType.type); + this._reportError(returnType.location, `invalid varying struct: ${returnType.type}`); } else { VisitorContext.context.varyingStruct = varyStruct.astNode; } + } else if (returnType.type !== EKeyword.VOID) { + this._reportError(returnType.location, "main entry can only return struct."); } const paramList = fnNode.protoType.parameterList; @@ -64,7 +70,7 @@ export abstract class GLESVisitor extends CodeGenVisitor { symbolType: ESymbolType.STRUCT }); if (!structSymbol) { - Logger.warn("no attribute struct found."); + this._reportError(paramInfo.astNode.location, `Not found attribute struct "${paramInfo.typeInfo.type}".`); continue; } VisitorContext.context.attributeStructs.push(structSymbol.astNode); diff --git a/packages/shader-lab/src/codeGen/VisitorContext.ts b/packages/shader-lab/src/codeGen/VisitorContext.ts index f537450227..3d6308f78e 100644 --- a/packages/shader-lab/src/codeGen/VisitorContext.ts +++ b/packages/shader-lab/src/codeGen/VisitorContext.ts @@ -1,9 +1,16 @@ -import { Logger } from "@galacean/engine"; import { EShaderStage } from "../common/Enums"; import { ASTNode } from "../parser/AST"; import { ESymbolType, SymbolTable, SymbolInfo } from "../parser/symbolTable"; import { IParamInfo } from "../parser/types"; - +import { GSErrorName } from "../GSError"; +import { BaseToken } from "../common/BaseToken"; +import { ShaderLab } from "../ShaderLab"; +import { ShaderLabUtils } from "../ShaderLabUtils"; +// #if _VERBOSE +import { GSError } from "../GSError"; +// #endif + +/** @internal */ export class VisitorContext { private static _singleton: VisitorContext; static get context() { @@ -30,12 +37,13 @@ export class VisitorContext { _curFn?: ASTNode.FunctionProtoType; _passSymbolTable: SymbolTable; + + private constructor() {} + get passSymbolTable() { return this._passSymbolTable; } - private constructor() {} - reset() { this.attributeList.length = 0; this.attributeStructs.length = 0; @@ -52,26 +60,34 @@ export class VisitorContext { return this.varyingStruct?.ident?.lexeme === type; } - referenceAttribute(ident: string) { - if (this._referencedAttributeList[ident]) return; + referenceAttribute(ident: BaseToken): GSError { + if (this._referencedAttributeList[ident.lexeme]) return; - const prop = this.attributeList.find((item) => item.ident.lexeme === ident); + const prop = this.attributeList.find((item) => item.ident.lexeme === ident.lexeme); if (!prop) { - Logger.error("referenced attribute not found:", ident); - return; + return ShaderLabUtils.createGSError( + `referenced attribute not found: ${ident.lexeme}`, + GSErrorName.CompilationError, + ShaderLab._processingPassText, + ident.location + ); } - this._referencedAttributeList[ident] = prop; + this._referencedAttributeList[ident.lexeme] = prop; } - referenceVarying(ident: string) { - if (this._referencedVaryingList[ident]) return; + referenceVarying(ident: BaseToken): GSError | undefined { + if (this._referencedVaryingList[ident.lexeme]) return; - const prop = this.varyingStruct?.propList.find((item) => item.ident.lexeme === ident); + const prop = this.varyingStruct?.propList.find((item) => item.ident.lexeme === ident.lexeme); if (!prop) { - Logger.error("referenced varying not found:", ident); - return; + return ShaderLabUtils.createGSError( + `referenced varying not found: ${ident.lexeme}`, + GSErrorName.CompilationError, + ShaderLab._processingPassText, + ident.location + ); } - this._referencedVaryingList[ident] = prop; + this._referencedVaryingList[ident.lexeme] = prop; } referenceGlobal(ident: string, type: ESymbolType) { diff --git a/packages/shader-lab/src/common/BaseScanner.ts b/packages/shader-lab/src/common/BaseScanner.ts index 87ca8f80a5..44c86d9f4b 100644 --- a/packages/shader-lab/src/common/BaseScanner.ts +++ b/packages/shader-lab/src/common/BaseScanner.ts @@ -1,10 +1,14 @@ import { ETokenType, ShaderRange, ShaderPosition } from "."; +import { GSErrorName } from "../GSError"; import { ShaderLab } from "../ShaderLab"; -import { ParserUtils } from "../Utils"; import { BaseToken } from "./BaseToken"; +import { ShaderLabUtils } from "../ShaderLabUtils"; export type OnToken = (token: BaseToken, scanner: BaseScanner) => void; +/** + * @internal + */ export default class BaseScanner { private static _spaceCharsWithBreak = [" ", "\t", "\n"]; private static _spaceChars = [" ", "\t"]; @@ -21,7 +25,7 @@ export default class BaseScanner { protected _currentIndex = 0; protected _source: string; - // #if _EDITOR + // #if _VERBOSE protected _column = 0; protected _line = 0; // #endif @@ -34,16 +38,26 @@ export default class BaseScanner { return this._source; } - get curPosition(): ShaderPosition { + getCurPosition(): ShaderPosition { return ShaderLab.createPosition( this._currentIndex, - // #if _EDITOR - this._column, - this._line + // #if _VERBOSE + this._line, + this._column // #endif ); } + // #if _VERBOSE + get line() { + return this._line; + } + + get column() { + return this._column; + } + // #endif + protected readonly _keywordsMap: Map; constructor(source: string, kws: Map = new Map()) { @@ -65,17 +79,12 @@ export default class BaseScanner { } } - /** - * @internal - */ _advance(): void { if (this.isEnd()) { return; } - this._currentIndex++; - - // #if _EDITOR + // #if _VERBOSE if (this.getCurChar() === "\n") { this._line += 1; this._column = 0; @@ -83,6 +92,8 @@ export default class BaseScanner { this._column += 1; } // #endif + + this._currentIndex++; } skipSpace(includeLineBreak: boolean): void { @@ -98,20 +109,20 @@ export default class BaseScanner { skipCommentsAndSpace(): ShaderRange | undefined { this.skipSpace(true); if (this.peek(2) === "//") { - const start = this.curPosition; + const start = this.getCurPosition(); this.advance(2); // single line comments while (this.getCurChar() !== "\n") this._advance(); this.skipCommentsAndSpace(); - return ShaderLab.createRange(start, this.curPosition); + return ShaderLab.createRange(start, this.getCurPosition()); } else if (this.peek(2) === "/*") { - const start = this.curPosition; + const start = this.getCurPosition(); this.advance(2); // multi-line comments while (this.peek(2) !== "*/" && !this.isEnd()) this._advance(); this.advance(2); this.skipCommentsAndSpace(); - return ShaderLab.createRange(start, this.curPosition); + return ShaderLab.createRange(start, this.getCurPosition()); } } @@ -124,11 +135,16 @@ export default class BaseScanner { this.skipCommentsAndSpace(); const peek = this.peek(text.length); if (peek !== text) { - ParserUtils.throw(this._currentIndex, `Expect ${text}, got ${peek}`); + this.throwError(this.getCurPosition(), `Expect text "${text}", but got "${peek}"`); } this.advance(text.length); } + throwError(pos: ShaderPosition | ShaderRange, ...msgs: any[]) { + const error = ShaderLabUtils.createGSError(msgs.join(" "), GSErrorName.ScannerError, this._source, pos); + throw error; + } + scanPairedText(left: string, right: string, balanced = false, skipLeading = false) { if (!skipLeading) { this.scanText(left); @@ -157,10 +173,10 @@ export default class BaseScanner { scanToken(onToken?: OnToken, splitCharRegex = /\w/) { this.skipCommentsAndSpace(); - const start = this.curPosition; + const start = this.getCurPosition(); if (this.isEnd()) return; while (splitCharRegex.test(this.getCurChar()) && !this.isEnd()) this._advance(); - const end = this.curPosition; + const end = this.getCurPosition(); if (start.index === end.index) { this._advance(); diff --git a/packages/shader-lab/src/common/BaseToken.ts b/packages/shader-lab/src/common/BaseToken.ts index 6a7b49541d..21d9a2aba8 100644 --- a/packages/shader-lab/src/common/BaseToken.ts +++ b/packages/shader-lab/src/common/BaseToken.ts @@ -1,11 +1,11 @@ import { ETokenType } from "./types"; import { ShaderRange, ShaderPosition } from "."; import { ShaderLab } from "../ShaderLab"; -import { ShaderLabObjectPool } from "../ShaderLabObjectPool"; import { IPoolElement } from "@galacean/engine"; +import { ShaderLabUtils } from "../ShaderLabUtils"; export class BaseToken implements IPoolElement { - static pool = new ShaderLabObjectPool(BaseToken); + static pool = ShaderLabUtils.createObjectPool(BaseToken); type: T; lexeme: string; @@ -22,9 +22,9 @@ export class BaseToken implements IPoolElement { } else { const end = ShaderLab.createPosition( arg.index + lexeme.length, - // #if _EDITOR + // #if _VERBOSE arg.line, - arg.column + arg.column + lexeme.length // #endif ); this.location = ShaderLab.createRange(arg, end); diff --git a/packages/shader-lab/src/common/ShaderPosition.ts b/packages/shader-lab/src/common/ShaderPosition.ts index 3b5ee0fc24..9dc3820b33 100644 --- a/packages/shader-lab/src/common/ShaderPosition.ts +++ b/packages/shader-lab/src/common/ShaderPosition.ts @@ -2,28 +2,30 @@ import { IPoolElement } from "@galacean/engine"; export class ShaderPosition implements IPoolElement { index: number; - // #if _EDITOR - line?: number; - column?: number; + // #if _VERBOSE + line: number; + column: number; // #endif - setX( + set( index: number, - /** #if _EDITOR */ - line?: number, - column?: number - /** #endif */ + // #if _VERBOSE + line: number, + column: number + // #endif ) { this.index = index; - /** #if _EDITOR */ + // #if _VERBOSE this.line = line; this.column = column; - /** #endif */ + // #endif } dispose(): void { this.index = 0; + // #if _VERBOSE this.line = 0; this.column = 0; + // #endif } } diff --git a/packages/shader-lab/src/common/ShaderRange.ts b/packages/shader-lab/src/common/ShaderRange.ts index e79d4143e0..6ead5e85d0 100644 --- a/packages/shader-lab/src/common/ShaderRange.ts +++ b/packages/shader-lab/src/common/ShaderRange.ts @@ -5,7 +5,7 @@ export class ShaderRange implements IPoolElement { public start: ShaderPosition; public end: ShaderPosition; - setX(start: ShaderPosition, end: ShaderPosition) { + set(start: ShaderPosition, end: ShaderPosition) { this.start = start; this.end = end; } diff --git a/packages/shader-lab/src/contentParser/Scanner.ts b/packages/shader-lab/src/contentParser/Scanner.ts index 63cdd33404..9661240cd8 100644 --- a/packages/shader-lab/src/contentParser/Scanner.ts +++ b/packages/shader-lab/src/contentParser/Scanner.ts @@ -25,4 +25,13 @@ export default class Scanner extends BaseScanner { } return Number(this._source.substring(start, this._currentIndex)); } + + // #if _VERBOSE + scanToCharacter(char: string): void { + while (this.getCurChar() !== char && !this.isEnd()) { + this._advance(); + } + this._advance(); + } + // #endif } diff --git a/packages/shader-lab/src/contentParser/ShaderContentParser.ts b/packages/shader-lab/src/contentParser/ShaderContentParser.ts index 3d4b4a03a7..72bd7bcd2b 100644 --- a/packages/shader-lab/src/contentParser/ShaderContentParser.ts +++ b/packages/shader-lab/src/contentParser/ShaderContentParser.ts @@ -23,7 +23,11 @@ import { IShaderPassContent, IRenderStates } from "@galacean/engine-design"; -import { ParserUtils } from "../Utils"; +import { GSErrorName } from "../GSError"; +// #if _VERBOSE +import { GSError } from "../GSError"; +import { ShaderLabUtils } from "../ShaderLabUtils"; +// #endif const EngineType = [ EKeyword.GS_RenderQueueType, @@ -44,8 +48,14 @@ const RenderStateType = [ EKeyword.GS_StencilState ]; +/** + * @internal + */ export class ShaderContentParser { static _engineType = { RenderQueueType, CompareFunction, StencilOperation, BlendOperation, BlendFactor, CullMode }; + + static _errors: GSError[] = []; + private static _isRenderStateDeclarator(token: BaseToken) { return RenderStateType.includes(token.type); } @@ -57,6 +67,7 @@ export class ShaderContentParser { private static _symbolTable: SymbolTableStack = new SymbolTableStack(); static reset() { + this._errors.length = 0; this._symbolTable.clear(); this._newScope(); } @@ -88,7 +99,6 @@ export class ShaderContentParser { for (let i = 0; i < subShader.passes.length; i++) { const pass = subShader.passes[i]; - // for (const pass of subShader.passes) { Object.assign(pass.renderStates.constantMap, constMap); Object.assign(pass.renderStates.variableMap, variableMap); if (pass.isUsePass) continue; @@ -105,7 +115,7 @@ export class ShaderContentParser { private static _parseShaderStatements(ret: IShaderContent, scanner: Scanner) { let braceLevel = 1; - let start = scanner.curPosition; + let start = scanner.getCurPosition(); while (true) { const word = scanner.scanToken(); @@ -114,20 +124,20 @@ export class ShaderContentParser { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); const subShader = this._parseSubShader(scanner); ret.subShaders.push(subShader); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case EKeyword.GS_EditorProperties: case EKeyword.GS_EditorMacros: this._addGlobalStatement(ret, scanner, start, word.lexeme.length); scanner.scanPairedText("{", "}", true); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case EKeyword.GS_RenderQueueType: this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseRenderQueueAssignment(ret, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case ETokenType.NOT_WORD: @@ -145,12 +155,12 @@ export class ShaderContentParser { if (ShaderContentParser._isRenderStateDeclarator(word)) { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseRenderStateDeclarationOrAssignment(ret, word, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; } else if (ShaderContentParser._isEngineType(word)) { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseVariableDeclaration(word.type, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; } } @@ -174,7 +184,16 @@ export class ShaderContentParser { scanner.scanText(";"); const sm = this._symbolTable.lookup({ type: stateToken.type, ident: variable.lexeme }); if (!sm?.value) { - ParserUtils.throw(scanner.current, `Invalid ${stateToken.lexeme} variable:`, variable.lexeme); + const error = ShaderLabUtils.createGSError( + `Invalid "${stateToken.lexeme}" variable: ${variable.lexeme}`, + GSErrorName.CompilationError, + scanner.source, + variable.location + ); + // #if _VERBOSE + this._errors.push(error); + return; + // #endif } const renderState = sm.value as IRenderStates; Object.assign(ret.renderStates.constantMap, renderState.constantMap); @@ -222,15 +241,36 @@ export class ShaderContentParser { scanner.scanText("]"); scanner.scanText("="); } else if (op.lexeme !== "=") { - ParserUtils.throw(scanner.current, "Invalid syntax, expect character '=', but got", op.lexeme); + const error = ShaderLabUtils.createGSError( + `Invalid syntax, expect character '=', but got ${op.lexeme}`, + GSErrorName.CompilationError, + scanner.source, + scanner.getCurPosition() + ); + // #if _VERBOSE + this._errors.push(error); + scanner.scanToCharacter(";"); + return; + // #endif } renderStateProp += idx; } renderStateProp = state + renderStateProp; const renderStateElementKey = RenderStateDataKey[renderStateProp]; - if (renderStateElementKey == undefined) - ParserUtils.throw(scanner.current, "Invalid render state element", renderStateProp); + if (renderStateElementKey == undefined) { + const error = ShaderLabUtils.createGSError( + `Invalid render state element ${renderStateProp}`, + GSErrorName.CompilationError, + scanner.source, + scanner.getCurPosition() + ); + // #if _VERBOSE + this._errors.push(error); + scanner.scanToCharacter(";"); + return; + // #endif + } scanner.skipCommentsAndSpace(); let value: any; @@ -258,8 +298,19 @@ export class ShaderContentParser { scanner._advance(); const engineTypeProp = scanner.scanToken(); value = ShaderContentParser._engineType[token.lexeme]?.[engineTypeProp.lexeme]; - if (value == undefined) - ParserUtils.throw(scanner.current, "Invalid engine constant:", `${token.lexeme}.${engineTypeProp.lexeme}`); + if (value == undefined) { + const error = ShaderLabUtils.createGSError( + `Invalid engine constant: ${token.lexeme}.${engineTypeProp.lexeme}`, + GSErrorName.CompilationError, + scanner.source, + engineTypeProp.location + ); + // #if _VERBOSE + this._errors.push(error); + scanner.scanToCharacter(";"); + return; + // #endif + } } else { value = token.lexeme; } @@ -278,7 +329,16 @@ export class ShaderContentParser { scanner.scanText(";"); const value = ShaderContentParser._engineType.RenderQueueType[word.lexeme]; if (value == undefined) { - ParserUtils.throw(scanner.current, "Invalid render queue", word.lexeme); + const error = ShaderLabUtils.createGSError( + `Invalid render queue ${word.lexeme}`, + GSErrorName.CompilationError, + scanner.source, + word.location + ); + // #if _VERBOSE + this._errors.push(error); + return; + // #endif } const key = RenderStateDataKey.RenderQueueType; ret.renderStates.constantMap[key] = value; @@ -292,7 +352,7 @@ export class ShaderContentParser { ) { if (scanner.current > start.index + offset) { ret.globalContents.push({ - range: { start, end: { ...scanner.curPosition, index: scanner.current - offset - 1 } }, + range: { start, end: { ...scanner.getCurPosition(), index: scanner.current - offset - 1 } }, content: scanner.source.substring(start.index, scanner.current - offset - 1) }); } @@ -311,7 +371,7 @@ export class ShaderContentParser { scanner.scanText("{"); scanner.skipCommentsAndSpace(); - let start = scanner.curPosition; + let start = scanner.getCurPosition(); while (true) { const word = scanner.scanToken(); @@ -320,13 +380,13 @@ export class ShaderContentParser { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); const pass = this._parsePass(scanner); ret.passes.push(pass); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case EKeyword.GS_RenderQueueType: this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseRenderQueueAssignment(ret, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case EKeyword.GS_UsePass: @@ -334,13 +394,13 @@ export class ShaderContentParser { const name = scanner.scanPairedText('"', '"'); // @ts-ignore ret.passes.push({ name, isUsePass: true, renderStates: { constantMap: {}, variableMap: {} }, tags: {} }); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case EKeyword.GS_Tags: this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseTags(ret, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case ETokenType.NOT_WORD: @@ -358,12 +418,12 @@ export class ShaderContentParser { if (ShaderContentParser._isRenderStateDeclarator(word)) { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseRenderStateDeclarationOrAssignment(ret, word, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; } else if (ShaderContentParser._isEngineType(word)) { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseVariableDeclaration(word.type, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; } } @@ -389,6 +449,7 @@ export class ShaderContentParser { } private static _parsePass(scanner: Scanner): IShaderPassContent { + this._newScope(); const ret = { globalContents: [], renderStates: { constantMap: {}, variableMap: {} }, @@ -401,7 +462,7 @@ export class ShaderContentParser { let braceLevel = 1; scanner.skipCommentsAndSpace(); - let start = scanner.curPosition; + let start = scanner.getCurPosition(); while (true) { const word = scanner.scanToken(); @@ -409,13 +470,13 @@ export class ShaderContentParser { case EKeyword.GS_RenderQueueType: this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseRenderQueueAssignment(ret, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case EKeyword.GS_Tags: this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseTags(ret, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case EKeyword.GS_VertexShader: @@ -423,15 +484,22 @@ export class ShaderContentParser { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); scanner.scanText("="); const entry = scanner.scanToken(); - // #if _EDITOR if (ret[word.lexeme]) { - ParserUtils.throw(scanner.current, "reassign main entry"); + const error = ShaderLabUtils.createGSError( + "reassign main entry", + GSErrorName.CompilationError, + scanner.source, + scanner.getCurPosition() + ); + // #if _VERBOSE + Logger.error(error.toString()); + throw error; + // #endif } - // #endif const key = word.type === EKeyword.GS_VertexShader ? "vertexEntry" : "fragmentEntry"; ret[key] = entry.lexeme; scanner.scanText(";"); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; case ETokenType.NOT_WORD: @@ -449,12 +517,12 @@ export class ShaderContentParser { if (ShaderContentParser._isRenderStateDeclarator(word)) { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseRenderStateDeclarationOrAssignment(ret, word, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; } else if (ShaderContentParser._isEngineType(word)) { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); this._parseVariableDeclaration(word.type, scanner); - start = scanner.curPosition; + start = scanner.getCurPosition(); break; } } diff --git a/packages/shader-lab/src/index.ts b/packages/shader-lab/src/index.ts index 7e733a06cc..637143b64d 100644 --- a/packages/shader-lab/src/index.ts +++ b/packages/shader-lab/src/index.ts @@ -1,15 +1,16 @@ export { ShaderLab } from "./ShaderLab"; -// #if _EDITOR +// #if _VERBOSE export { Preprocessor } from "./preprocessor"; +export * from "./GSError"; // #endif //@ts-ignore export const version = `__buildVersion`; let mode = "Release"; -// #if _EDITOR -mode = "Editor"; +// #if _VERBOSE +mode = "Verbose"; // #endif console.log(`Galacean ShaderLab version: ${version}. mode: ${mode}`); diff --git a/packages/shader-lab/src/lalr/CFG.ts b/packages/shader-lab/src/lalr/CFG.ts index f199f95b00..01ff210182 100644 --- a/packages/shader-lab/src/lalr/CFG.ts +++ b/packages/shader-lab/src/lalr/CFG.ts @@ -121,7 +121,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.storage_qualifier, [[EKeyword.CONST], [EKeyword.IN], [EKeyword.INOUT], [EKeyword.OUT], [EKeyword.CENTROID]], - // #if _EDITOR + // #if _VERBOSE ASTNode.StorageQualifier.pool // #endif ), @@ -129,7 +129,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.interpolation_qualifier, [[EKeyword.SMOOTH], [EKeyword.FLAT]], - // #if _EDITOR + // #if _VERBOSE ASTNode.InterpolationQualifier.pool // #endif ), @@ -137,7 +137,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.invariant_qualifier, [[EKeyword.INVARIANT]], - // #if _EDITOR + // #if _VERBOSE ASTNode.InvariantQualifier.pool // #endif ), @@ -145,7 +145,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.precision_qualifier, [[EKeyword.HIGHP], [EKeyword.MEDIUMP], [EKeyword.LOWP]], - // #if _EDITOR + // #if _VERBOSE ASTNode.PrecisionQualifier.pool // #endif ), @@ -253,7 +253,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ENonTerminal.assignment_expression ] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.ConditionalExpression.pool // #endif ), @@ -264,7 +264,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.logical_xor_expression], [ENonTerminal.logical_or_expression, ETokenType.OR_OP, ENonTerminal.logical_xor_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.LogicalOrExpression.pool // #endif ), @@ -275,7 +275,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.logical_and_expression], [ENonTerminal.logical_xor_expression, ETokenType.XOR_OP, ENonTerminal.logical_and_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.LogicalXorExpression.pool // #endif ), @@ -286,7 +286,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.inclusive_or_expression], [ENonTerminal.logical_and_expression, ETokenType.AND_OP, ENonTerminal.inclusive_or_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.LogicalAndExpression.pool // #endif ), @@ -297,7 +297,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.exclusive_or_expression], [ENonTerminal.inclusive_or_expression, ETokenType.VERTICAL_BAR, ENonTerminal.exclusive_or_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.InclusiveOrExpression.pool // #endif ), @@ -308,7 +308,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.and_expression], [ENonTerminal.exclusive_or_expression, ETokenType.CARET, ENonTerminal.and_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.ExclusiveOrExpression.pool // #endif ), @@ -319,7 +319,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.equality_expression], [ENonTerminal.and_expression, ETokenType.AMPERSAND, ENonTerminal.equality_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.AndExpression.pool // #endif ), @@ -331,7 +331,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.equality_expression, ETokenType.EQ_OP, ENonTerminal.relational_expression], [ENonTerminal.equality_expression, ETokenType.NE_OP, ENonTerminal.relational_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.EqualityExpression.pool // #endif ), @@ -345,7 +345,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.relational_expression, ETokenType.LE_OP, ENonTerminal.shift_expression], [ENonTerminal.relational_expression, ETokenType.GE_OP, ENonTerminal.shift_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.RelationalExpression.pool // #endif ), @@ -357,7 +357,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.shift_expression, ETokenType.LEFT_OP, ENonTerminal.additive_expression], [ENonTerminal.shift_expression, ETokenType.RIGHT_OP, ENonTerminal.additive_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.ShiftExpression.pool // #endif ), @@ -369,7 +369,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.additive_expression, ETokenType.PLUS, ENonTerminal.multiplicative_expression], [ENonTerminal.additive_expression, ETokenType.DASH, ENonTerminal.multiplicative_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.AdditiveExpression.pool // #endif ), @@ -382,7 +382,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.multiplicative_expression, ETokenType.SLASH, ENonTerminal.unary_expression], [ENonTerminal.multiplicative_expression, ETokenType.PERCENT, ENonTerminal.unary_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.MultiplicativeExpression.pool // #endif ), @@ -395,7 +395,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ETokenType.DEC_OP, ENonTerminal.unary_expression], [ENonTerminal.unary_operator, ENonTerminal.unary_expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.UnaryExpression.pool // #endif ), @@ -403,7 +403,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.unary_operator, [[ETokenType.PLUS], [ETokenType.DASH], [ETokenType.BANG], [ETokenType.TILDE]], - // #if _EDITOR + // #if _VERBOSE ASTNode.UnaryOperator.pool // #endif ), @@ -468,7 +468,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ETokenType.XOR_ASSIGN], [ETokenType.OR_ASSIGN] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.AssignmentOperator.pool // #endif ), @@ -566,7 +566,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.statement, [[ENonTerminal.compound_statement], [ENonTerminal.simple_statement]], - // #if _EDITOR + // #if _VERBOSE ASTNode.Statement.pool // #endif ), @@ -586,7 +586,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ETokenType.LEFT_BRACE, ETokenType.RIGHT_BRACE], [ENonTerminal.scope_brace, ENonTerminal.statement_list, ENonTerminal.scope_end_brace] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.CompoundStatement.pool // #endif ), @@ -600,7 +600,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.iteration_statement], [ENonTerminal.jump_statement] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.SimpleStatement.pool // #endif ), @@ -673,7 +673,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.assignment_expression], [ETokenType.LEFT_BRACE, ENonTerminal.initializer_list, ETokenType.RIGHT_BRACE] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.Initializer.pool // #endif ), @@ -681,7 +681,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.initializer_list, [[ENonTerminal.initializer], [ENonTerminal.initializer_list, ETokenType.COMMA, ENonTerminal.initializer]], - // #if _EDITOR + // #if _VERBOSE ASTNode.InitializerList.pool // #endif ), @@ -689,7 +689,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.expression_statement, [[ETokenType.SEMICOLON], [ENonTerminal.expression, ETokenType.SEMICOLON]], - // #if _EDITOR + // #if _VERBOSE ASTNode.ExpressionStatement.pool // #endif ), @@ -709,7 +709,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ENonTerminal.statement ] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.SelectionStatement.pool // #endif ), @@ -727,7 +727,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ENonTerminal.statement ] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.IterationStatement.pool // #endif ), @@ -748,7 +748,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.for_init_statement, [[ENonTerminal.expression_statement], [ENonTerminal.declaration]], - // #if _EDITOR + // #if _VERBOSE ASTNode.ForInitStatement.pool // #endif ), @@ -759,7 +759,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.expression], [ENonTerminal.fully_specified_type, ETokenType.ID, ETokenType.EQUAL, ENonTerminal.initializer] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.Condition.pool // #endif ), @@ -770,7 +770,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ [ENonTerminal.conditionopt, ETokenType.SEMICOLON], [ENonTerminal.conditionopt, ETokenType.SEMICOLON, ENonTerminal.expression] ], - // #if _EDITOR + // #if _VERBOSE ASTNode.ForRestStatement.pool // #endif ), @@ -778,7 +778,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( ENonTerminal.conditionopt, [[ETokenType.EPSILON], [ENonTerminal.condition]], - // #if _EDITOR + // #if _VERBOSE ASTNode.ConditionOpt.pool // #endif ), diff --git a/packages/shader-lab/src/lalr/LALR1.ts b/packages/shader-lab/src/lalr/LALR1.ts index ac489891e4..b83eb78f60 100644 --- a/packages/shader-lab/src/lalr/LALR1.ts +++ b/packages/shader-lab/src/lalr/LALR1.ts @@ -161,7 +161,7 @@ export class LALR1 { if (terminal === EKeyword.ELSE && exist.action === EAction.Shift && action.action === EAction.Reduce) { return; } else { - // #if _EDITOR + // #if _VERBOSE console.warn( `conflict detect: `, Utils.printAction(exist), diff --git a/packages/shader-lab/src/lalr/StateItem.ts b/packages/shader-lab/src/lalr/StateItem.ts index 16e7e66fd1..ccf6bbb845 100644 --- a/packages/shader-lab/src/lalr/StateItem.ts +++ b/packages/shader-lab/src/lalr/StateItem.ts @@ -59,13 +59,13 @@ export default class StateItem { } advance() { - // #if _EDITOR + // #if _VERBOSE if (this.canReduce()) throw `Error: advance reduce-able parsing state item`; // #endif return new StateItem(this.production, this.position + 1, this.lookaheadSet); } - // #if _EDITOR + // #if _VERBOSE toString() { const coreItem = this.production.derivation.map((item) => GrammarUtils.toString(item)); coreItem[this.position] = "." + (coreItem[this.position] ?? ""); diff --git a/packages/shader-lab/src/lalr/Utils.ts b/packages/shader-lab/src/lalr/Utils.ts index 3aed2648cc..8eb9492fef 100644 --- a/packages/shader-lab/src/lalr/Utils.ts +++ b/packages/shader-lab/src/lalr/Utils.ts @@ -5,8 +5,7 @@ import { ENonTerminal, GrammarSymbol } from "../parser/GrammarSymbol"; import Production from "./Production"; import { ActionInfo, EAction } from "./types"; import { ShaderLab } from "../ShaderLab"; -import { ShaderLabObjectPool } from "../ShaderLabObjectPool"; -import { IPoolElement } from "@galacean/engine"; +import { ClearableObjectPool, IPoolElement } from "@galacean/engine"; import { NodeChild } from "../parser/types"; export default class GrammarUtils { @@ -25,8 +24,7 @@ export default class GrammarUtils { goal: ENonTerminal, options: GrammarSymbol[][], /** the ast node */ - // astType?: ASTNodeConstructor - astTypePool?: ShaderLabObjectPool< + astTypePool?: ClearableObjectPool< { set: (loc: ShaderRange, children: NodeChild[]) => void } & IPoolElement & TreeNode > ) { @@ -74,6 +72,7 @@ export default class GrammarUtils { return a.action === b.action && a.target === b.target; } + // #if _VERBOSE static printAction(actionInfo: ActionInfo) { return ` ${ actionInfo.action === EAction.Reduce ? Production.pool.get(actionInfo.target!) : `State ${actionInfo.target!}` @@ -84,4 +83,5 @@ export default class GrammarUtils { const deriv = production.derivation.map((gs) => GrammarUtils.toString(gs)).join("|"); return `${ENonTerminal[production.goal]} :=> ${deriv}`; } + // #endif } diff --git a/packages/shader-lab/src/lexer/Lexer.ts b/packages/shader-lab/src/lexer/Lexer.ts index 346e7788f6..326aa79da1 100644 --- a/packages/shader-lab/src/lexer/Lexer.ts +++ b/packages/shader-lab/src/lexer/Lexer.ts @@ -12,7 +12,7 @@ export class Lexer extends BaseScanner { reset(source: string) { this._source = source; this._currentIndex = 0; - // #if _EDITOR + // #if _VERBOSE this._line = this._column = 0; // #endif } @@ -278,8 +278,7 @@ export class Lexer extends BaseScanner { return this._scanStringConst(); default: - console.log("at position", start); - throw `Unexpected character ${this.getCurChar()}`; + this.throwError(this.getCurPosition(), `Unexpected character ${this.getCurChar()}`); } return token; } @@ -314,7 +313,7 @@ export class Lexer extends BaseScanner { private _getPosition(offset /** offset from starting point */ = 0) { return ShaderLab.createPosition( this.current - offset, - // #if _EDITOR + // #if _VERBOSE this._line, this._column - offset // #endif @@ -385,7 +384,8 @@ export class Lexer extends BaseScanner { buffer.push(this.getCurChar()); this.advance(); } - if (!LexerUtils.isNum(this.getCurChar())) throw "lexing error, invalid exponent suffix."; + if (!LexerUtils.isNum(this.getCurChar())) + this.throwError(this.getCurPosition(), "lexing error, invalid exponent suffix."); while (LexerUtils.isNum(this.getCurChar())) { buffer.push(this.getCurChar()); this.advance(); diff --git a/packages/shader-lab/src/parser/AST.ts b/packages/shader-lab/src/parser/AST.ts index 1ebad0decf..f01ffbdce6 100644 --- a/packages/shader-lab/src/parser/AST.ts +++ b/packages/shader-lab/src/parser/AST.ts @@ -1,4 +1,4 @@ -// #if _EDITOR +// #if _VERBOSE import { BuiltinFunction, BuiltinVariable, NonGenericGalaceanType } from "./builtin"; // #endif import { CodeGenVisitor } from "../codeGen"; @@ -8,10 +8,10 @@ import { EKeyword, ETokenType, TokenType, ShaderRange, GalaceanDataType, TypeAny import SematicAnalyzer from "./SemanticAnalyzer"; import { ShaderData } from "./ShaderInfo"; import { ESymbolType, FnSymbol, StructSymbol, VarSymbol } from "./symbolTable"; -import { ParserUtils } from "../Utils"; +import { ParserUtils } from "../ParserUtils"; import { IParamInfo, NodeChild, StructProp, SymbolType } from "./types"; -import { ShaderLabObjectPool } from "../ShaderLabObjectPool"; -import { IPoolElement } from "@galacean/engine"; +import { ClearableObjectPool, IPoolElement } from "@galacean/engine"; +import { ShaderLabUtils } from "../ShaderLabUtils"; export abstract class TreeNode implements IPoolElement { /** The non-terminal in grammar. */ @@ -44,7 +44,7 @@ export abstract class TreeNode implements IPoolElement { } export namespace ASTNode { - export type ASTNodePool = ShaderLabObjectPool< + export type ASTNodePool = ClearableObjectPool< { set: (loc: ShaderRange, children: NodeChild[]) => void } & IPoolElement & TreeNode >; @@ -63,7 +63,7 @@ export namespace ASTNode { } export class TrivialNode extends TreeNode { - static pool = new ShaderLabObjectPool(TrivialNode); + static pool = ShaderLabUtils.createObjectPool(TrivialNode); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal._ignore); @@ -71,7 +71,7 @@ export namespace ASTNode { } export class ScopeBrace extends TreeNode { - static pool = new ShaderLabObjectPool(ScopeBrace); + static pool = ShaderLabUtils.createObjectPool(ScopeBrace); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.scope_brace); @@ -83,7 +83,7 @@ export namespace ASTNode { } export class ScopeEndBrace extends TreeNode { - static pool = new ShaderLabObjectPool(ScopeEndBrace); + static pool = ShaderLabUtils.createObjectPool(ScopeEndBrace); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.scope_end_brace); @@ -95,13 +95,13 @@ export namespace ASTNode { } export class JumpStatement extends TreeNode { - static pool = new ShaderLabObjectPool(JumpStatement); + static pool = ShaderLabUtils.createObjectPool(JumpStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.jump_statement); } - // #if _EDITOR + // #if _VERBOSE override semanticAnalyze(sa: SematicAnalyzer): void { if (ASTNode._unwrapToken(this.children![0]).type === EKeyword.RETURN) { // TODO: check the equality of function return type declared and this type. @@ -114,9 +114,9 @@ export namespace ASTNode { } } - // #if _EDITOR + // #if _VERBOSE export class ConditionOpt extends TreeNode { - static pool = new ShaderLabObjectPool(ConditionOpt); + static pool = ShaderLabUtils.createObjectPool(ConditionOpt); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.conditionopt); @@ -124,7 +124,7 @@ export namespace ASTNode { } export class ForRestStatement extends TreeNode { - static pool = new ShaderLabObjectPool(ForRestStatement); + static pool = ShaderLabUtils.createObjectPool(ForRestStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.for_rest_statement); @@ -132,7 +132,7 @@ export namespace ASTNode { } export class Condition extends TreeNode { - static pool = new ShaderLabObjectPool(Condition); + static pool = ShaderLabUtils.createObjectPool(Condition); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.condition); @@ -140,7 +140,7 @@ export namespace ASTNode { } export class ForInitStatement extends TreeNode { - static pool = new ShaderLabObjectPool(ForInitStatement); + static pool = ShaderLabUtils.createObjectPool(ForInitStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.for_init_statement); @@ -148,7 +148,7 @@ export namespace ASTNode { } export class IterationStatement extends TreeNode { - static pool = new ShaderLabObjectPool(IterationStatement); + static pool = ShaderLabUtils.createObjectPool(IterationStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.iteration_statement); @@ -156,7 +156,7 @@ export namespace ASTNode { } export class SelectionStatement extends TreeNode { - static pool = new ShaderLabObjectPool(SelectionStatement); + static pool = ShaderLabUtils.createObjectPool(SelectionStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.selection_statement); @@ -164,7 +164,7 @@ export namespace ASTNode { } export class ExpressionStatement extends TreeNode { - static pool = new ShaderLabObjectPool(ExpressionStatement); + static pool = ShaderLabUtils.createObjectPool(ExpressionStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.expression_statement); @@ -187,9 +187,9 @@ export namespace ASTNode { } } - // #if _EDITOR + // #if _VERBOSE export class InitializerList extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(InitializerList); + static pool = ShaderLabUtils.createObjectPool(InitializerList); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.initializer_list); @@ -202,7 +202,7 @@ export namespace ASTNode { } export class Initializer extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(Initializer); + static pool = ShaderLabUtils.createObjectPool(Initializer); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.initializer); @@ -219,7 +219,7 @@ export namespace ASTNode { // #endif export class SingleDeclaration extends TreeNode { - static pool = new ShaderLabObjectPool(SingleDeclaration); + static pool = ShaderLabUtils.createObjectPool(SingleDeclaration); typeSpecifier: TypeSpecifier; arraySpecifier?: ArraySpecifier; @@ -258,7 +258,7 @@ export namespace ASTNode { } export class FullySpecifiedType extends TreeNode { - static pool = new ShaderLabObjectPool(FullySpecifiedType); + static pool = ShaderLabUtils.createObjectPool(FullySpecifiedType); get qualifierList() { if (this.children.length > 1) { @@ -280,7 +280,7 @@ export namespace ASTNode { } export class TypeQualifier extends TreeNode { - static pool = new ShaderLabObjectPool(TypeQualifier); + static pool = ShaderLabUtils.createObjectPool(TypeQualifier); qualifierList: EKeyword[]; @@ -301,7 +301,7 @@ export namespace ASTNode { } export class SingleTypeQualifier extends TreeNode { - static pool = new ShaderLabObjectPool(SingleTypeQualifier); + static pool = ShaderLabUtils.createObjectPool(SingleTypeQualifier); qualifier: EKeyword; lexeme: string; @@ -335,9 +335,9 @@ export namespace ASTNode { } } - // #if _EDITOR + // #if _VERBOSE export class StorageQualifier extends BasicTypeQualifier { - static pool = new ShaderLabObjectPool(StorageQualifier); + static pool = ShaderLabUtils.createObjectPool(StorageQualifier); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.storage_qualifier); @@ -345,7 +345,7 @@ export namespace ASTNode { } export class PrecisionQualifier extends BasicTypeQualifier { - static pool = new ShaderLabObjectPool(PrecisionQualifier); + static pool = ShaderLabUtils.createObjectPool(PrecisionQualifier); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.precision_qualifier); @@ -353,7 +353,7 @@ export namespace ASTNode { } export class InterpolationQualifier extends BasicTypeQualifier { - static pool = new ShaderLabObjectPool(InterpolationQualifier); + static pool = ShaderLabUtils.createObjectPool(InterpolationQualifier); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.interpolation_qualifier); @@ -361,7 +361,7 @@ export namespace ASTNode { } export class InvariantQualifier extends BasicTypeQualifier { - static pool = new ShaderLabObjectPool(InvariantQualifier); + static pool = ShaderLabUtils.createObjectPool(InvariantQualifier); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.invariant_qualifier); @@ -370,7 +370,7 @@ export namespace ASTNode { // #endif export class TypeSpecifier extends TreeNode { - static pool = new ShaderLabObjectPool(TypeSpecifier); + static pool = ShaderLabUtils.createObjectPool(TypeSpecifier); get type(): GalaceanDataType { return (this.children![0] as TypeSpecifierNonArray).type; @@ -392,7 +392,7 @@ export namespace ASTNode { } export class ArraySpecifier extends TreeNode { - static pool = new ShaderLabObjectPool(ArraySpecifier); + static pool = ShaderLabUtils.createObjectPool(ArraySpecifier); get size(): number | undefined { const integerConstantExpr = this.children[1] as IntegerConstantExpression; @@ -405,7 +405,7 @@ export namespace ASTNode { } export class IntegerConstantExpressionOperator extends TreeNode { - static pool = new ShaderLabObjectPool(IntegerConstantExpressionOperator); + static pool = ShaderLabUtils.createObjectPool(IntegerConstantExpressionOperator); compute: (a: number, b: number) => number; get lexeme(): string { @@ -435,13 +435,13 @@ export namespace ASTNode { this.compute = (a, b) => a % b; break; default: - throw `not implemented operator ${operator.lexeme}`; + sa.error(operator.location, `not implemented operator ${operator.lexeme}`); } } } export class IntegerConstantExpression extends TreeNode { - static pool = new ShaderLabObjectPool(IntegerConstantExpression); + static pool = ShaderLabUtils.createObjectPool(IntegerConstantExpression); value?: number; override set(loc: ShaderRange, children: NodeChild[]) { @@ -455,14 +455,14 @@ export namespace ASTNode { if (child instanceof Token) { this.value = Number(child.lexeme); } - // #if _EDITOR + // #if _VERBOSE else { const id = child as VariableIdentifier; if (!id.symbolInfo) { - sa.error(id.location, "undeclared symbol:", id.lexeme); + sa.error(id.location, "Undeclared symbol:", id.lexeme); } if (!ParserUtils.typeCompatible(EKeyword.INT, id.typeInfo)) { - sa.error(id.location, "invalid integer."); + sa.error(id.location, "Invalid integer."); return; } } @@ -472,7 +472,7 @@ export namespace ASTNode { } export class TypeSpecifierNonArray extends TreeNode { - static pool = new ShaderLabObjectPool(TypeSpecifierNonArray); + static pool = ShaderLabUtils.createObjectPool(TypeSpecifierNonArray); type: GalaceanDataType; lexeme: string; @@ -490,7 +490,7 @@ export namespace ASTNode { } export class ExtBuiltinTypeSpecifierNonArray extends TreeNode { - static pool = new ShaderLabObjectPool(ExtBuiltinTypeSpecifierNonArray); + static pool = ShaderLabUtils.createObjectPool(ExtBuiltinTypeSpecifierNonArray); type: TokenType; lexeme: string; @@ -504,7 +504,7 @@ export namespace ASTNode { } export class InitDeclaratorList extends TreeNode { - static pool = new ShaderLabObjectPool(InitDeclaratorList); + static pool = ShaderLabUtils.createObjectPool(InitDeclaratorList); get typeInfo(): SymbolType { if (this.children.length === 1) { @@ -533,9 +533,9 @@ export namespace ASTNode { } else if (this.children.length === 4 || this.children.length === 6) { const typeInfo = this.typeInfo; const arraySpecifier = this.children[3] as ArraySpecifier; - // #if _EDITOR + // #if _VERBOSE if (typeInfo.arraySpecifier && arraySpecifier) { - sa.error(arraySpecifier.location, "array of array is not supported."); + sa.error(arraySpecifier.location, "Array of array is not supported."); } // #endif typeInfo.arraySpecifier = arraySpecifier; @@ -547,7 +547,7 @@ export namespace ASTNode { } export class IdentifierList extends TreeNode { - static pool = new ShaderLabObjectPool(IdentifierList); + static pool = ShaderLabUtils.createObjectPool(IdentifierList); get idList(): Token[] { if (this.children.length === 2) { @@ -562,7 +562,7 @@ export namespace ASTNode { } export class Declaration extends TreeNode { - static pool = new ShaderLabObjectPool(Declaration); + static pool = ShaderLabUtils.createObjectPool(Declaration); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.declaration); @@ -574,7 +574,7 @@ export namespace ASTNode { } export class FunctionProtoType extends TreeNode { - static pool = new ShaderLabObjectPool(FunctionProtoType); + static pool = ShaderLabUtils.createObjectPool(FunctionProtoType); private get declarator() { return this.children[0] as FunctionDeclarator; @@ -606,7 +606,7 @@ export namespace ASTNode { } export class FunctionDeclarator extends TreeNode { - static pool = new ShaderLabObjectPool(FunctionDeclarator); + static pool = ShaderLabUtils.createObjectPool(FunctionDeclarator); private get header() { return this.children[0] as FunctionHeader; @@ -638,7 +638,7 @@ export namespace ASTNode { } export class FunctionHeader extends TreeNode { - static pool = new ShaderLabObjectPool(FunctionHeader); + static pool = ShaderLabUtils.createObjectPool(FunctionHeader); get ident() { return this.children[1] as Token; @@ -661,7 +661,7 @@ export namespace ASTNode { } export class FunctionParameterList extends TreeNode { - static pool = new ShaderLabObjectPool(FunctionParameterList); + static pool = ShaderLabUtils.createObjectPool(FunctionParameterList); get parameterInfoList(): IParamInfo[] { if (this.children.length === 1) { @@ -694,7 +694,7 @@ export namespace ASTNode { } export class ParameterDeclaration extends TreeNode { - static pool = new ShaderLabObjectPool(ParameterDeclaration); + static pool = ShaderLabUtils.createObjectPool(ParameterDeclaration); get typeQualifier() { if (this.children.length === 2) return this.children[0] as TypeQualifier; @@ -730,7 +730,7 @@ export namespace ASTNode { } export class ParameterDeclarator extends TreeNode { - static pool = new ShaderLabObjectPool(ParameterDeclarator); + static pool = ShaderLabUtils.createObjectPool(ParameterDeclarator); get ident() { return this.children[1] as Token; @@ -747,9 +747,9 @@ export namespace ASTNode { } } - // #if _EDITOR + // #if _VERBOSE export class SimpleStatement extends TreeNode { - static pool = new ShaderLabObjectPool(SimpleStatement); + static pool = ShaderLabUtils.createObjectPool(SimpleStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.simple_statement); @@ -757,7 +757,7 @@ export namespace ASTNode { } export class CompoundStatement extends TreeNode { - static pool = new ShaderLabObjectPool(CompoundStatement); + static pool = ShaderLabUtils.createObjectPool(CompoundStatement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.compound_statement); @@ -766,16 +766,16 @@ export namespace ASTNode { // #endif export class CompoundStatementNoScope extends TreeNode { - static pool = new ShaderLabObjectPool(CompoundStatementNoScope); + static pool = ShaderLabUtils.createObjectPool(CompoundStatementNoScope); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.compound_statement_no_scope); } } - // #if _EDITOR + // #if _VERBOSE export class Statement extends TreeNode { - static pool = new ShaderLabObjectPool(Statement); + static pool = ShaderLabUtils.createObjectPool(Statement); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.statement); @@ -784,7 +784,7 @@ export namespace ASTNode { // #endif export class StatementList extends TreeNode { - static pool = new ShaderLabObjectPool(StatementList); + static pool = ShaderLabUtils.createObjectPool(StatementList); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.statement_list); @@ -796,7 +796,7 @@ export namespace ASTNode { } export class FunctionDefinition extends TreeNode { - static pool = new ShaderLabObjectPool(FunctionDefinition); + static pool = ShaderLabUtils.createObjectPool(FunctionDefinition); get protoType() { return this.children[0] as FunctionProtoType; @@ -822,7 +822,7 @@ export namespace ASTNode { } export class FunctionCall extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(FunctionCall); + static pool = ShaderLabUtils.createObjectPool(FunctionCall); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.function_call); @@ -838,7 +838,7 @@ export namespace ASTNode { } export class FunctionCallGeneric extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(FunctionCallGeneric); + static pool = ShaderLabUtils.createObjectPool(FunctionCallGeneric); fnSymbol: FnSymbol | StructSymbol | undefined; @@ -861,7 +861,7 @@ export namespace ASTNode { paramSig = paramList.paramSig as any; } } - // #if _EDITOR + // #if _VERBOSE const builtinFn = BuiltinFunction.getFn(fnIdent, ...(paramSig ?? [])); if (builtinFn) { this.type = BuiltinFunction.getReturnType(builtinFn.fun, builtinFn.genType); @@ -871,8 +871,8 @@ export namespace ASTNode { const fnSymbol = sa.symbolTable.lookup({ ident: fnIdent, symbolType: ESymbolType.FN, signature: paramSig }); if (!fnSymbol) { - // #if _EDITOR - sa.error(this.location, "no overload function type found:", functionIdentifier.ident); + // #if _VERBOSE + sa.error(this.location, "No overload function type found: ", functionIdentifier.ident); // #endif return; } @@ -883,7 +883,7 @@ export namespace ASTNode { } export class FunctionCallParameterList extends TreeNode { - static pool = new ShaderLabObjectPool(FunctionCallParameterList); + static pool = ShaderLabUtils.createObjectPool(FunctionCallParameterList); get paramSig(): GalaceanDataType[] | undefined { if (this.children.length === 1) { @@ -918,7 +918,7 @@ export namespace ASTNode { } export class PrecisionSpecifier extends TreeNode { - static pool = new ShaderLabObjectPool(PrecisionSpecifier); + static pool = ShaderLabUtils.createObjectPool(PrecisionSpecifier); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.precision_specifier); @@ -930,7 +930,7 @@ export namespace ASTNode { } export class FunctionIdentifier extends TreeNode { - static pool = new ShaderLabObjectPool(FunctionIdentifier); + static pool = ShaderLabUtils.createObjectPool(FunctionIdentifier); get ident() { const ty = this.children[0] as TypeSpecifier; @@ -958,13 +958,13 @@ export namespace ASTNode { } export class AssignmentExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(AssignmentExpression); + static pool = ShaderLabUtils.createObjectPool(AssignmentExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.assignment_expression); } - // #if _EDITOR + // #if _VERBOSE override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { const expr = this.children[0] as ConditionalExpression; @@ -977,9 +977,9 @@ export namespace ASTNode { // #endif } - // #if _EDITOR + // #if _VERBOSE export class AssignmentOperator extends TreeNode { - static pool = new ShaderLabObjectPool(AssignmentOperator); + static pool = ShaderLabUtils.createObjectPool(AssignmentOperator); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.assignment_operator); @@ -988,13 +988,13 @@ export namespace ASTNode { // #endif export class Expression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(Expression); + static pool = ShaderLabUtils.createObjectPool(Expression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.expression); } - // #if _EDITOR + // #if _VERBOSE override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { const expr = this.children[0] as AssignmentExpression; @@ -1008,7 +1008,7 @@ export namespace ASTNode { } export class PrimaryExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(PrimaryExpression); + static pool = ShaderLabUtils.createObjectPool(PrimaryExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.primary_expression); @@ -1041,7 +1041,7 @@ export namespace ASTNode { } export class PostfixExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(PostfixExpression); + static pool = ShaderLabUtils.createObjectPool(PostfixExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.postfix_expression); @@ -1056,9 +1056,9 @@ export namespace ASTNode { } } - // #if _EDITOR + // #if _VERBOSE export class UnaryOperator extends TreeNode { - static pool = new ShaderLabObjectPool(UnaryOperator); + static pool = ShaderLabUtils.createObjectPool(UnaryOperator); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.unary_operator); @@ -1066,9 +1066,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class UnaryExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(UnaryExpression); + static pool = ShaderLabUtils.createObjectPool(UnaryExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.unary_expression); @@ -1077,9 +1077,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class MultiplicativeExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(MultiplicativeExpression); + static pool = ShaderLabUtils.createObjectPool(MultiplicativeExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.multiplicative_expression); @@ -1096,9 +1096,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class AdditiveExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(AdditiveExpression); + static pool = ShaderLabUtils.createObjectPool(AdditiveExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.additive_expression); @@ -1115,9 +1115,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class ShiftExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(ShiftExpression); + static pool = ShaderLabUtils.createObjectPool(ShiftExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.shift_expression); @@ -1130,9 +1130,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class RelationalExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(RelationalExpression); + static pool = ShaderLabUtils.createObjectPool(RelationalExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.relational_expression); @@ -1148,9 +1148,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class EqualityExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(EqualityExpression); + static pool = ShaderLabUtils.createObjectPool(EqualityExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.equality_expression); @@ -1166,9 +1166,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class AndExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(AndExpression); + static pool = ShaderLabUtils.createObjectPool(AndExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.and_expression); @@ -1184,9 +1184,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class ExclusiveOrExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(ExclusiveOrExpression); + static pool = ShaderLabUtils.createObjectPool(ExclusiveOrExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.exclusive_or_expression); @@ -1202,9 +1202,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class InclusiveOrExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(InclusiveOrExpression); + static pool = ShaderLabUtils.createObjectPool(InclusiveOrExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.inclusive_or_expression); @@ -1220,9 +1220,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class LogicalAndExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(LogicalAndExpression); + static pool = ShaderLabUtils.createObjectPool(LogicalAndExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.logical_and_expression); @@ -1238,9 +1238,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class LogicalXorExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(LogicalXorExpression); + static pool = ShaderLabUtils.createObjectPool(LogicalXorExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.logical_xor_expression); @@ -1256,9 +1256,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class LogicalOrExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(LogicalOrExpression); + static pool = ShaderLabUtils.createObjectPool(LogicalOrExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.logical_or_expression); @@ -1274,9 +1274,9 @@ export namespace ASTNode { } // #endif - // #if _EDITOR + // #if _VERBOSE export class ConditionalExpression extends ExpressionAstNode { - static pool = new ShaderLabObjectPool(ConditionalExpression); + static pool = ShaderLabUtils.createObjectPool(ConditionalExpression); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.conditional_expression); @@ -1291,7 +1291,7 @@ export namespace ASTNode { // #endif export class StructSpecifier extends TreeNode { - static pool = new ShaderLabObjectPool(StructSpecifier); + static pool = ShaderLabUtils.createObjectPool(StructSpecifier); ident?: Token; @@ -1313,7 +1313,7 @@ export namespace ASTNode { } export class StructDeclarationList extends TreeNode { - static pool = new ShaderLabObjectPool(StructDeclarationList); + static pool = ShaderLabUtils.createObjectPool(StructDeclarationList); get propList(): StructProp[] { if (this.children.length === 1) { @@ -1330,7 +1330,7 @@ export namespace ASTNode { } export class StructDeclaration extends TreeNode { - static pool = new ShaderLabObjectPool(StructDeclaration); + static pool = ShaderLabUtils.createObjectPool(StructDeclaration); get typeSpecifier() { if (this.children.length === 3) { @@ -1363,7 +1363,7 @@ export namespace ASTNode { } export class StructDeclaratorList extends TreeNode { - static pool = new ShaderLabObjectPool(StructDeclaratorList); + static pool = ShaderLabUtils.createObjectPool(StructDeclaratorList); get declaratorList(): StructDeclarator[] { if (this.children.length === 1) { @@ -1380,7 +1380,7 @@ export namespace ASTNode { } export class StructDeclarator extends TreeNode { - static pool = new ShaderLabObjectPool(StructDeclarator); + static pool = ShaderLabUtils.createObjectPool(StructDeclarator); get ident() { return this.children[0] as Token; @@ -1396,7 +1396,7 @@ export namespace ASTNode { } export class VariableDeclaration extends TreeNode { - static pool = new ShaderLabObjectPool(VariableDeclaration); + static pool = ShaderLabUtils.createObjectPool(VariableDeclaration); override set(loc: ShaderRange, children: NodeChild[]) { super.set(loc, children, ENonTerminal.variable_declaration); @@ -1417,11 +1417,11 @@ export namespace ASTNode { } export class VariableIdentifier extends TreeNode { - static pool = new ShaderLabObjectPool(VariableIdentifier); + static pool = ShaderLabUtils.createObjectPool(VariableIdentifier); symbolInfo: | VarSymbol - // #if _EDITOR + // #if _VERBOSE | BuiltinVariable // #endif | null; @@ -1442,7 +1442,7 @@ export namespace ASTNode { override semanticAnalyze(sa: SematicAnalyzer): void { const token = this.children[0] as Token; - // #if _EDITOR + // #if _VERBOSE const builtinVar = BuiltinVariable.getVar(token.lexeme); if (builtinVar) { this.symbolInfo = builtinVar; @@ -1451,7 +1451,7 @@ export namespace ASTNode { // #endif this.symbolInfo = sa.symbolTable.lookup({ ident: token.lexeme, symbolType: ESymbolType.VAR }) as VarSymbol; - // #if _EDITOR + // #if _VERBOSE if (!this.symbolInfo) { sa.error(this.location, "undeclared identifier:", token.lexeme); } @@ -1464,7 +1464,7 @@ export namespace ASTNode { } export class GLShaderProgram extends TreeNode { - static pool = new ShaderLabObjectPool(GLShaderProgram); + static pool = ShaderLabUtils.createObjectPool(GLShaderProgram); shaderData: ShaderData; diff --git a/packages/shader-lab/src/parser/SemanticAnalyzer.ts b/packages/shader-lab/src/parser/SemanticAnalyzer.ts index ea7b0487bf..b078287d97 100644 --- a/packages/shader-lab/src/parser/SemanticAnalyzer.ts +++ b/packages/shader-lab/src/parser/SemanticAnalyzer.ts @@ -1,13 +1,14 @@ import { ShaderRange } from "../common"; import { TreeNode } from "./AST"; -// #if _EDITOR -import { SemanticError } from "../Error"; -// #endif +import { GSErrorName } from "../GSError"; import { ShaderData } from "./ShaderInfo"; import { SymbolInfo, SymbolTable } from "../parser/symbolTable"; import { NodeChild } from "./types"; import { SymbolTableStack } from "../common/BaseSymbolTable"; -import { Logger } from "@galacean/engine"; +import { ShaderLab } from "../ShaderLab"; +// #if _VERBOSE +import { GSError } from "../GSError"; +// #endif export type TranslationRule = (sa: SematicAnalyzer, ...tokens: NodeChild[]) => T; @@ -22,8 +23,8 @@ export default class SematicAnalyzer { symbolTable: SymbolTableStack = new SymbolTableStack(); private _shaderData = new ShaderData(); - // #if _EDITOR - readonly errors: SemanticError[] = []; + // #if _VERBOSE + readonly errors: GSError[] = []; // #endif get shaderData() { @@ -41,7 +42,7 @@ export default class SematicAnalyzer { this._shaderData = new ShaderData(); this.symbolTable.clear(); this.newScope(); - // #if _EDITOR + // #if _VERBOSE this.errors.length = 0; // #endif } @@ -63,13 +64,13 @@ export default class SematicAnalyzer { return this._translationRuleTable.get(pid); } - // #if _EDITOR error(loc: ShaderRange, ...param: any[]) { - Logger.error(loc, ...param); - - const err = new SemanticError(param.join(""), loc); + // #if _VERBOSE + const err = new GSError(GSErrorName.CompilationError, param.join(""), loc, ShaderLab._processingPassText); this.errors.push(err); return err; + // #else + throw new Error(param.join("")); + // #endif } - // #endif } diff --git a/packages/shader-lab/src/parser/ShaderTargetParser.ts b/packages/shader-lab/src/parser/ShaderTargetParser.ts index 56250740b7..e0b70f8b66 100644 --- a/packages/shader-lab/src/parser/ShaderTargetParser.ts +++ b/packages/shader-lab/src/parser/ShaderTargetParser.ts @@ -8,8 +8,11 @@ import SematicAnalyzer from "./SemanticAnalyzer"; import { TraceStackItem } from "./types"; import { addTranslationRule, createGrammar } from "../lalr/CFG"; import { LALR1 } from "../lalr"; -import { ParserUtils } from "../Utils"; +import { ParserUtils } from "../ParserUtils"; import { Logger } from "@galacean/engine"; +import { GSErrorName } from "../GSError"; +import { ShaderLab } from "../ShaderLab"; +import { ShaderLabUtils } from "../ShaderLabUtils"; /** * The syntax parser and sematic analyzer of `ShaderLab` compiler @@ -31,6 +34,13 @@ export class ShaderTargetParser { return this.gotoTable.get(this.curState); } + // #if _VERBOSE + /** @internal */ + get errors() { + return this.sematicAnalyzer.errors; + } + // #endif + static _singleton: ShaderTargetParser; static create() { @@ -103,13 +113,21 @@ export class ShaderTargetParser { traceBackStack.push(nextState); continue; } else { - Logger.error(token.location, `parse error token ${token.lexeme}`); + const error = ShaderLabUtils.createGSError( + `Unexpected token ${token.lexeme}`, + GSErrorName.CompilationError, + ShaderLab._processingPassText, + token.location + ); + // #if _VERBOSE + this.sematicAnalyzer.errors.push(error); return null; + // #endif } } } - // #if _EDITOR + // #if _VERBOSE private _printStack(nextToken: BaseToken) { let str = ""; for (let i = 0; i < this._traceBackStack.length - 1; i++) { diff --git a/packages/shader-lab/src/parser/builtin/functions.ts b/packages/shader-lab/src/parser/builtin/functions.ts index d6b5d4dcf3..c65b1883eb 100644 --- a/packages/shader-lab/src/parser/builtin/functions.ts +++ b/packages/shader-lab/src/parser/builtin/functions.ts @@ -126,18 +126,16 @@ BuiltinFunction._create("ceil", EGenType.GenType, EGenType.GenType); BuiltinFunction._create("fract", EGenType.GenType, EGenType.GenType); BuiltinFunction._create("mod", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT); BuiltinFunction._create("mod", EGenType.GenType, EGenType.GenType, EGenType.GenType); -BuiltinFunction._create("min", EGenType.GenType, EGenType.GenType); -BuiltinFunction._create("min", EGenType.GenType, EKeyword.FLOAT); -BuiltinFunction._create("min", EGenType.GenIntType, EGenType.GenIntType); -BuiltinFunction._create("min", EGenType.GenIntType, EKeyword.INT); +BuiltinFunction._create("min", EGenType.GenType, EGenType.GenType, EGenType.GenType); +BuiltinFunction._create("min", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT); +BuiltinFunction._create("min", EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType); +BuiltinFunction._create("min", EGenType.GenIntType, EGenType.GenIntType, EKeyword.INT); BuiltinFunction._create("min", EGenType.GenUintType, EGenType.GenUintType, EGenType.GenUintType); BuiltinFunction._create("min", EGenType.GenUintType, EGenType.GenUintType, EKeyword.UINT); -BuiltinFunction._create("max", EGenType.GenType, EGenType.GenType); -BuiltinFunction._create("max", EGenType.GenType, EKeyword.FLOAT); -BuiltinFunction._create("max", EGenType.GenIntType, EGenType.GenIntType); -BuiltinFunction._create("max", EGenType.GenIntType, EKeyword.INT); -BuiltinFunction._create("max", EGenType.GenUintType, EGenType.GenUintType, EGenType.GenUintType); -BuiltinFunction._create("max", EGenType.GenUintType, EGenType.GenUintType, EKeyword.UINT); +BuiltinFunction._create("max", EGenType.GenType, EGenType.GenType, EGenType.GenType); +BuiltinFunction._create("max", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT); +BuiltinFunction._create("max", EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType); +BuiltinFunction._create("max", EGenType.GenIntType, EGenType.GenIntType, EKeyword.INT); BuiltinFunction._create("clamp", EGenType.GenType, EGenType.GenType, EGenType.GenType, EGenType.GenType); BuiltinFunction._create("clamp", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT, EKeyword.FLOAT); BuiltinFunction._create("clamp", EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType); diff --git a/packages/shader-lab/src/parser/builtin/index.ts b/packages/shader-lab/src/parser/builtin/index.ts index f613228147..618db58261 100644 --- a/packages/shader-lab/src/parser/builtin/index.ts +++ b/packages/shader-lab/src/parser/builtin/index.ts @@ -1,4 +1,4 @@ -// #if _EDITOR +// #if _VERBOSE export * from "./functions"; export * from "./variables"; // #endif diff --git a/packages/shader-lab/src/preprocessor/MacroDefine.ts b/packages/shader-lab/src/preprocessor/MacroDefine.ts index e2cca2fa8b..56db10d50d 100644 --- a/packages/shader-lab/src/preprocessor/MacroDefine.ts +++ b/packages/shader-lab/src/preprocessor/MacroDefine.ts @@ -1,49 +1,37 @@ import { BaseToken } from "../common/BaseToken"; import { ShaderRange } from "../common"; -import { ParserUtils } from "../Utils"; +import { ShaderLabUtils } from "../ShaderLabUtils"; +// #if _VERBOSE +import { GSErrorName } from "../GSError"; +// #endif export class MacroDefine { - readonly location?: ShaderRange; - readonly macro: BaseToken; - readonly args?: BaseToken[]; - readonly body?: BaseToken; + private _replaceRegex?: RegExp; + private readonly _argsLexemes: string[]; - get isFunction() { + get isFunction(): boolean { return !!this.args?.length; } - get macroLexeme() { - return this.macro.lexeme; - } - - constructor(macro: BaseToken, body?: BaseToken, loc?: ShaderRange, args?: BaseToken[]) { - this.location = loc; - this.macro = macro; - this.body = body; - this.args = args; + constructor( + public readonly macro: BaseToken, + public readonly body?: BaseToken, + public readonly location?: ShaderRange, + public readonly args?: BaseToken[] + ) { + if (args) { + this._argsLexemes = this.args.map((item) => item.lexeme); + this._replaceRegex = new RegExp(`\\b(${this._argsLexemes.join("|")})\\b`, "g"); + } } - private _expand(...args: string[]): string { - if (this.isFunction) { - const argsTextList = this.args!.map((item) => item.lexeme); - - // #if _EDITOR - if (args.length !== this.args?.length) { - ParserUtils.throw(this.location, "mismatched function macro"); - } - // #endif - const replaceRegex = new RegExp(`\\b(${argsTextList.join("|")})\\b`, "g"); - return this.body.lexeme.replaceAll(replaceRegex, (_, m) => { - const idx = argsTextList.findIndex((item) => item === m); - return args[idx]; - }); + expandFunctionBody(args: string[]): string { + if (args.length !== this.args?.length) { + throw ShaderLabUtils.createGSError("mismatched function macro", GSErrorName.PreprocessorError, "", this.location); } - return this.body.lexeme; - } - expand(...args: string[]): string { - const ret = this._expand(...args); - // TODO: erase the comments, any more performant and lightweight solution? - return ret.replaceAll(/(\/\/[^\n]*|\/\*.*\*\/)/gs, ""); + return this.body.lexeme.replace(this._replaceRegex, (m) => { + return args[this._argsLexemes.indexOf(m)]; + }); } } diff --git a/packages/shader-lab/src/preprocessor/PpParser.ts b/packages/shader-lab/src/preprocessor/PpParser.ts index 025d311cc0..afe5e8dd4b 100644 --- a/packages/shader-lab/src/preprocessor/PpParser.ts +++ b/packages/shader-lab/src/preprocessor/PpParser.ts @@ -1,19 +1,20 @@ -import { ShaderRange } from "../common"; +import { ShaderPosition, ShaderRange } from "../common"; import LexerUtils from "../lexer/Utils"; import { MacroDefine } from "./MacroDefine"; -// #if _EDITOR -import PpSourceMap, { BlockInfo } from "./sourceMap"; -// #endif import { BaseToken } from "../common/BaseToken"; -import { ParserUtils } from "../Utils"; import { EPpKeyword, EPpToken, PpConstant } from "./constants"; import PpScanner from "./PpScanner"; import { PpUtils } from "./Utils"; import { ShaderLab } from "../ShaderLab"; import { ShaderPass } from "@galacean/engine"; +import { ShaderLabUtils } from "../ShaderLabUtils"; +import { GSErrorName } from "../GSError"; +// #if _VERBOSE +import PpSourceMap, { BlockInfo } from "./sourceMap"; +// #endif export interface ExpandSegment { - // #if _EDITOR + // #if _VERBOSE block?: BlockInfo; // #endif rangeInBlock: ShaderRange; @@ -21,7 +22,7 @@ export interface ExpandSegment { } /** @internal */ -export default class PpParser { +export class PpParser { private static _definedMacros: Map = new Map(); private static _expandSegmentsStack: ExpandSegment[][] = [[]]; @@ -31,6 +32,10 @@ export default class PpParser { private static _includeMap: Record; private static _basePathForIncludeKey: string; + // #if _VERBOSE + static _errors: Error[] = []; + // #endif + static reset(includeMap: Record, basePathForIncludeKey: string) { this._definedMacros.clear(); this._expandSegmentsStack.length = 0; @@ -39,6 +44,9 @@ export default class PpParser { this.addPredefinedMacro("GL_ES"); this._includeMap = includeMap; this._basePathForIncludeKey = basePathForIncludeKey; + // #if _VERBOSE + this._errors.length = 0; + // #endif } static addPredefinedMacro(macro: string, value?: string) { @@ -54,7 +62,7 @@ export default class PpParser { this._definedMacros.set(macro, new MacroDefine(tk, macroBody)); } - static parse(scanner: PpScanner): string { + static parse(scanner: PpScanner): string | null { while (!scanner.isEnd()) { const directive = scanner.scanDirective(this._onToken.bind(this))!; if (scanner.isEnd()) break; @@ -84,6 +92,9 @@ export default class PpParser { break; } } + // #if _VERBOSE + if (this._errors.length > 0) return null; + // #endif return PpUtils.expand(this.expandSegments, scanner.source, scanner.sourceMap); } @@ -92,6 +103,11 @@ export default class PpParser { return this._expandSegmentsStack[this._expandSegmentsStack.length - 1]; } + private static reportError(loc: ShaderRange | ShaderPosition, message: string, source: string, file: string) { + const error = ShaderLabUtils.createGSError(message, GSErrorName.PreprocessorError, source, loc, file); + this._errors.push(error); + } + private static _parseInclude(scanner: PpScanner) { const start = scanner.getShaderPosition(8); @@ -111,16 +127,17 @@ export default class PpParser { const end = scanner.getShaderPosition(); const chunk = this._includeMap[includedPath]; if (!chunk) { - ParserUtils.throw(id.location, `Shader slice "${includedPath}" not founded.`); + this.reportError(id.location, `Shader slice "${includedPath}" not founded.`, scanner.source, scanner.file); + return; } const range = ShaderLab.createRange(start, end); const expanded = this._expandMacroChunk(chunk, range, id.lexeme); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(id.lexeme, undefined, expanded.sourceMap); // #endif this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, @@ -144,13 +161,13 @@ export default class PpParser { const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap); // #endif const range = ShaderLab.createRange(bodyChunk.location.start, end); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, @@ -179,13 +196,13 @@ export default class PpParser { if (directive === EPpKeyword.else) { const { token: elseChunk } = scanner.scanMacroBranchChunk(); const expanded = this._expandMacroChunk(elseChunk.lexeme, elseChunk.location, scanner); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap); // #endif const startPosition = ShaderLab.createPosition(start); const range = ShaderLab.createRange(startPosition, scanner.getShaderPosition()); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, @@ -197,28 +214,28 @@ export default class PpParser { if (!!constantExpr) { const end = nextDirective.type === EPpKeyword.endif ? scanner.current : scanner.scanRemainMacro().index; const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap); // #endif const startPosition = ShaderLab.createPosition(start); const endPosition = ShaderLab.createPosition(end); const range = ShaderLab.createRange(startPosition, endPosition); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, replace: expanded.content }); } else { - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange); // #endif const startPosition = ShaderLab.createPosition(start); const endPosition = ShaderLab.createPosition(scanner.current); const range = ShaderLab.createRange(startPosition, endPosition); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, @@ -285,7 +302,8 @@ export default class PpParser { scanner.skipSpace(false); const operand2 = this._parseRelationalExpression(scanner) as number; if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") { - ParserUtils.throw(opPos, "invalid operator in relation expression."); + this.reportError(opPos, "invalid operator in relation expression.", scanner.source, scanner.file); + return; } switch (operator) { case ">": @@ -310,7 +328,8 @@ export default class PpParser { scanner.skipSpace(false); const operand2 = this._parseShiftExpression(scanner) as number; if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") { - ParserUtils.throw(opPos, "invalid operator in shift expression."); + this.reportError(opPos, "invalid operator in shift expression.", scanner.source, scanner.file); + return; } switch (operator) { case ">>": @@ -333,7 +352,8 @@ export default class PpParser { scanner.skipSpace(false); const operand2 = this._parseAdditiveExpression(scanner) as number; if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") { - ParserUtils.throw(opPos, "invalid operator."); + this.reportError(opPos, "invalid operator.", scanner.source, scanner.file); + return false; } switch (operator) { case "+": @@ -354,7 +374,8 @@ export default class PpParser { scanner.skipSpace(false); const operand2 = this._parseMulticativeExpression(scanner) as number; if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") { - ParserUtils.throw(opPos, "invalid operator."); + this.reportError(opPos, "invalid operator.", scanner.source, scanner.file); + return; } switch (operator) { case "*": @@ -375,7 +396,7 @@ export default class PpParser { const opPos = scanner.getShaderPosition(); const parenExpr = this._parseParenthesisExpression(scanner); if ((operator === "!" && typeof parenExpr !== "boolean") || (operator !== "!" && typeof parenExpr !== "number")) { - ParserUtils.throw(opPos, "invalid operator."); + this.reportError(opPos, "invalid operator.", scanner.source, scanner.file); } switch (operator) { @@ -417,15 +438,14 @@ export default class PpParser { } else { const macro = this._definedMacros.get(id.lexeme); if (!macro) { - // ParserUtils.throw(id.location, 'undefined macro:', id.lexeme); return false; } if (macro.isFunction) { - ParserUtils.throw(id.location, "invalid function macro usage"); + this.reportError(id.location, "invalid function macro usage", scanner.source, scanner.file); } const value = Number(macro.body.lexeme); if (!Number.isInteger(value)) { - ParserUtils.throw(id.location, "invalid const macro:", id.lexeme); + this.reportError(id.location, `invalid const macro: ${id.lexeme}`, scanner.source, scanner.file); } this._branchMacros.add(id.lexeme); return value; @@ -434,7 +454,12 @@ export default class PpParser { const integer = scanner.scanInteger(); return Number(integer.lexeme); } else { - ParserUtils.throw(scanner.getShaderPosition(), "invalid token", scanner.getCurChar()); + this.reportError( + scanner.getShaderPosition(), + `invalid token: ${scanner.getCurChar()}`, + scanner.source, + scanner.file + ); } } @@ -447,7 +472,7 @@ export default class PpParser { parentScanner: PpScanner ): { content: string; - // #if _EDITOR + // #if _VERBOSE sourceMap: PpSourceMap; // #endif }; @@ -457,7 +482,7 @@ export default class PpParser { file: string ): { content: string; - // #if _EDITOR + // #if _VERBOSE sourceMap: PpSourceMap; // #endif }; @@ -467,7 +492,7 @@ export default class PpParser { scannerOrFile: PpScanner | string ): { content: string; - // #if _EDITOR + // #if _VERBOSE sourceMap: PpSourceMap; // #endif } { @@ -482,7 +507,7 @@ export default class PpParser { this._expandSegmentsStack.pop(); return { content: ret, - // #if _EDITOR + // #if _VERBOSE sourceMap: scanner.sourceMap // #endif }; @@ -501,12 +526,12 @@ export default class PpParser { const end = nextDirective.type === EPpKeyword.endif ? scanner.getShaderPosition() : scanner.scanRemainMacro(); const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner); - // #if _EDITOR + // #if _VERBOSE const blockInfo = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap); // #endif const range = ShaderLab.createRange(bodyChunk.location.start, end); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block: blockInfo, // #endif rangeInBlock: range, @@ -521,14 +546,14 @@ export default class PpParser { } private static _addEmptyReplace(scanner: PpScanner, start: number) { - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange); // #endif const startPosition = ShaderLab.createPosition(start); - const endPosition = scanner.curPosition; + const endPosition = scanner.getCurPosition(); const range = ShaderLab.createRange(startPosition, endPosition); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, @@ -546,12 +571,12 @@ export default class PpParser { if (!!constantExpr) { const end = nextDirective.type === EPpKeyword.endif ? scanner.getShaderPosition() : scanner.scanRemainMacro(); const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap); // #endif const range = ShaderLab.createRange(bodyChunk.location.start, end); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, @@ -571,7 +596,7 @@ export default class PpParser { let end = macro.location.end; if (this._definedMacros.get(macro.lexeme) && macro.lexeme.startsWith("GL_")) { - ParserUtils.throw(macro.location, "redefined macro:", macro.lexeme); + this.reportError(macro.location, `redefined macro: ${macro.lexeme}`, scanner.source, scanner.file); } let macroArgs: BaseToken[] | undefined; @@ -584,15 +609,15 @@ export default class PpParser { const macroDefine = new MacroDefine(macro, macroBody, range, macroArgs); this._definedMacros.set(macro.lexeme, macroDefine); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange); // #endif this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif - rangeInBlock: ShaderLab.createRange(start, scanner.curPosition), + rangeInBlock: ShaderLab.createRange(start, scanner.getCurPosition()), replace: "" }); } @@ -601,13 +626,13 @@ export default class PpParser { const start = scanner.current - 6; const macro = scanner.scanWord(); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange); // #endif const startPosition = ShaderLab.createPosition(start); - const range = ShaderLab.createRange(startPosition, scanner.curPosition); + const range = ShaderLab.createRange(startPosition, scanner.getCurPosition()); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, @@ -659,15 +684,15 @@ export default class PpParser { args.push(scanner.source.slice(curIdx, scanner.current)); scanner.advance(); - const range = ShaderLab.createRange(token.location!.start, scanner.curPosition); - replace = macro.expand(...args); + const range = ShaderLab.createRange(token.location!.start, scanner.getCurPosition()); + replace = macro.expandFunctionBody(args); const expanded = this._expandMacroChunk(replace, range, scanner); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap); // #endif - const blockRange = ShaderLab.createRange(token.location!.start, scanner.curPosition); + const blockRange = ShaderLab.createRange(token.location!.start, scanner.getCurPosition()); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: blockRange, @@ -675,12 +700,12 @@ export default class PpParser { }); } else { const expanded = this._expandMacroChunk(replace, token.location, scanner); - // #if _EDITOR + // #if _VERBOSE const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap); // #endif const range = ShaderLab.createRange(token.location.start, token.location.end); this.expandSegments.push({ - // #if _EDITOR + // #if _VERBOSE block, // #endif rangeInBlock: range, diff --git a/packages/shader-lab/src/preprocessor/PpScanner.ts b/packages/shader-lab/src/preprocessor/PpScanner.ts index 892f8b87d3..5df0945b5c 100644 --- a/packages/shader-lab/src/preprocessor/PpScanner.ts +++ b/packages/shader-lab/src/preprocessor/PpScanner.ts @@ -1,11 +1,10 @@ import { ShaderRange, ShaderPosition } from "../common"; import LexerUtils from "../lexer/Utils"; -// #if _EDITOR +// #if _VERBOSE import PpSourceMap from "./sourceMap"; // #endif import BaseScanner from "../common/BaseScanner"; import { BaseToken, EOF } from "../common/BaseToken"; -import { ParserUtils } from "../Utils"; import { EPpKeyword, EPpToken, PpKeyword } from "./constants"; import { PpUtils } from "./Utils"; import { ShaderLab } from "../ShaderLab"; @@ -15,12 +14,9 @@ export type OnToken = (token: BaseToken, scanner: PpScanner) => void; export default class PpScanner extends BaseScanner { private static _splitCharacters = /[\w#.]/; - private line: number = 0; - private column: number = 0; - private macroLvl = 0; - // #if _EDITOR + // #if _VERBOSE readonly sourceMap = new PpSourceMap(); readonly file: string; readonly blockRange?: ShaderRange; @@ -28,13 +24,13 @@ export default class PpScanner extends BaseScanner { constructor( source: string, - // #if _EDITOR + // #if _VERBOSE file = "__main__", blockRange?: ShaderRange // #endif ) { super(source); - // #if _EDITOR + // #if _VERBOSE this.file = file; this.blockRange = blockRange; // #endif @@ -90,7 +86,7 @@ export default class PpScanner extends BaseScanner { const end = this._currentIndex; const word = this._source.slice(start, end); if (end === start) { - ParserUtils.throw(this.getShaderPosition(), "no word found."); + this.throwError(this.getShaderPosition(), "no word found."); } const kw = PpKeyword.get(word); if (kw) { @@ -105,7 +101,13 @@ export default class PpScanner extends BaseScanner { } getShaderPosition(offset /** offset from starting point */ = 0) { - return ShaderLab.createPosition(this._currentIndex - offset, this.line, this.column - offset); + return ShaderLab.createPosition( + this._currentIndex - offset, + // #if _VERBOSE + this.line, + this.column - offset + // #endif + ); } /** @@ -144,14 +146,14 @@ export default class PpScanner extends BaseScanner { scanQuotedString(): BaseToken { this.skipSpace(true); if (this.getCurChar() !== '"') { - ParserUtils.throw(this.getShaderPosition(), "unexpected char, expected '\"'"); + this.throwError(this.getShaderPosition(), "unexpected char, expected '\"'"); } const ShaderPosition = this.getShaderPosition(); this._advance(); const start = this._currentIndex; while (this.getCurChar() !== '"' && !this.isEnd()) this._advance(); if (this.isEnd()) { - ParserUtils.throw(this.getShaderPosition(), "unexpected char, expected '\"'"); + this.throwError(this.getShaderPosition(), "unexpected char, expected '\"'"); } const word = this._source.slice(start, this._currentIndex); @@ -233,7 +235,7 @@ export default class PpScanner extends BaseScanner { this.advance(); } if (this._currentIndex === start) { - ParserUtils.throw(this.getShaderPosition(), "no integer found"); + this.throwError(this.getShaderPosition(), "no integer found"); } const integer = this._source.slice(start, this._currentIndex); @@ -296,7 +298,7 @@ export default class PpScanner extends BaseScanner { while (this.getCurChar() !== "\n" && !this.isEnd()) { this._advance(); } - return ShaderLab.createRange(start, this.curPosition); + return ShaderLab.createRange(start, this.getCurPosition()); } else if (this.peek(2) === "/*") { const start = this.getShaderPosition(); // multi-line comments diff --git a/packages/shader-lab/src/preprocessor/Preprocessor.ts b/packages/shader-lab/src/preprocessor/Preprocessor.ts index 35021f393e..846e0166dd 100644 --- a/packages/shader-lab/src/preprocessor/Preprocessor.ts +++ b/packages/shader-lab/src/preprocessor/Preprocessor.ts @@ -1,4 +1,4 @@ -import PpParser from "./PpParser"; +import { PpParser } from "./PpParser"; import PpScanner from "./PpScanner"; /** @internal */ @@ -16,7 +16,7 @@ export class Preprocessor { /** * Should call it after reset. */ - static process(source: string): string { + static process(source: string): string | null { this.baseScanner = new PpScanner(source); return PpParser.parse(this.baseScanner); } @@ -25,7 +25,7 @@ export class Preprocessor { PpParser.addPredefinedMacro(macro, value); } - // #if _EDITOR + // #if _VERBOSE static convertSourceIndex(index: number) { return this.baseScanner.sourceMap.map(index); } diff --git a/packages/shader-lab/src/preprocessor/Utils.ts b/packages/shader-lab/src/preprocessor/Utils.ts index aec8474356..cb66422a19 100644 --- a/packages/shader-lab/src/preprocessor/Utils.ts +++ b/packages/shader-lab/src/preprocessor/Utils.ts @@ -1,6 +1,6 @@ import { ShaderRange } from "../common"; import { ExpandSegment } from "./PpParser"; -// #if _EDITOR +// #if _VERBOSE import PpSourceMap, { MapRange } from "./sourceMap"; // #endif @@ -8,7 +8,7 @@ export class PpUtils { static expand( segments: ExpandSegment[], source: string, - // #if _EDITOR + // #if _VERBOSE sourceMap?: PpSourceMap //#endif ) { @@ -22,7 +22,7 @@ export class PpUtils { const generatedIdxEnd = generatedIdx + originSlice.length + seg.replace.length; - // #if _EDITOR + // #if _VERBOSE const mapRange = new MapRange(seg.block, seg.rangeInBlock, { start: generatedIdx + originSlice.length, end: generatedIdxEnd diff --git a/packages/shader-lab/src/preprocessor/sourceMap/index.ts b/packages/shader-lab/src/preprocessor/sourceMap/index.ts index d932da13d6..44ea26abc9 100644 --- a/packages/shader-lab/src/preprocessor/sourceMap/index.ts +++ b/packages/shader-lab/src/preprocessor/sourceMap/index.ts @@ -1,6 +1,6 @@ import { ShaderRange } from "../../common/ShaderRange"; -// #if _EDITOR +// #if _VERBOSE export class BlockInfo { readonly sourceFile: string; readonly rangeInFile?: ShaderRange; diff --git a/packages/shader-lab/verbose/package.json b/packages/shader-lab/verbose/package.json new file mode 100644 index 0000000000..6a46602bdd --- /dev/null +++ b/packages/shader-lab/verbose/package.json @@ -0,0 +1,14 @@ +{ + "license": "MIT", + "main": "../dist/main.verbose.js", + "module": "../dist/module.verbose.js", + "browser": "../dist/browser.verbose.min.js", + "debug": "../src/index.ts", + "types": "../types/index.d.ts", + "umd": { + "name": "Galacean.ShaderLab", + "globals": { + "@galacean/engine": "Galacean" + } + } +} diff --git a/rollup.config.js b/rollup.config.js index f616b01aec..ad02bf0645 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,7 +8,6 @@ import serve from "rollup-plugin-serve"; import miniProgramPlugin from "./rollup.miniprogram.plugin"; import replace from "@rollup/plugin-replace"; import { swc, defineRollupSwcOption, minify } from "rollup-plugin-swc3"; -import modify from "rollup-plugin-modify"; import jscc from "rollup-plugin-jscc"; const { BUILD_TYPE, NODE_ENV } = process.env; @@ -26,6 +25,9 @@ const pkgs = fs }; }); +const shaderLabPkg = pkgs.find((item) => item.pkgJson.name === "@galacean/engine-shader-lab"); +pkgs.push({ ...shaderLabPkg, verboseMode: true }); + // toGlobalName const extensions = [".js", ".jsx", ".ts", ".tsx"]; @@ -49,9 +51,6 @@ const commonPlugins = [ }) ), commonjs(), - jscc({ - values: { _EDITOR: NODE_ENV !== "release" } - }), NODE_ENV === "development" ? serve({ contentBase: "packages", @@ -60,11 +59,19 @@ const commonPlugins = [ : null ]; -function config({ location, pkgJson }) { +function config({ location, pkgJson, verboseMode }) { const input = path.join(location, "src", "index.ts"); const dependencies = Object.assign({}, pkgJson.dependencies ?? {}, pkgJson.peerDependencies ?? {}); + const curPlugins = Array.from(commonPlugins); + + curPlugins.push( + jscc({ + values: { _VERBOSE: verboseMode } + }) + ); + const external = Object.keys(dependencies); - commonPlugins.push( + curPlugins.push( replace({ preventAssignment: true, __buildVersion: pkgJson.version @@ -76,16 +83,20 @@ function config({ location, pkgJson }) { const umdConfig = pkgJson.umd; let file = path.join(location, "dist", "browser.js"); - const plugins = [ - modify({ - find: "chevrotain", - replace: path.join(process.cwd(), "packages", "shader-lab", `./node_modules/chevrotain/lib/chevrotain.js`) - }), - ...commonPlugins - ]; - if (compress) { - plugins.push(minify({ sourceMap: true })); - file = path.join(location, "dist", "browser.min.js"); + if (verboseMode) { + if (compress) { + curPlugins.push(minify({ sourceMap: true })); + file = path.join(location, "dist", "browser.verbose.min.js"); + } else { + file = path.join(location, "dist", "browser.verbose.js"); + } + } else { + if (compress) { + curPlugins.push(minify({ sourceMap: true })); + file = path.join(location, "dist", "browser.min.js"); + } else { + file = path.join(location, "dist", "browser.js"); + } } const umdExternal = Object.keys(umdConfig.globals ?? {}); @@ -102,17 +113,21 @@ function config({ location, pkgJson }) { globals: umdConfig.globals } ], - plugins + plugins: curPlugins }; }, mini: () => { - const plugins = [...commonPlugins, ...miniProgramPlugin]; + let file = path.join(location, "dist", "miniprogram.js"); + const plugins = [...curPlugins, ...miniProgramPlugin]; + if (verboseMode) { + file = path.join(location, "dist", "miniprogram.verbose.js"); + } return { input, output: [ { format: "cjs", - file: path.join(location, "dist/miniprogram.js"), + file, sourcemap: false } ], @@ -121,29 +136,28 @@ function config({ location, pkgJson }) { }; }, module: () => { - const plugins = [ - modify({ - find: "chevrotain", - replace: path.join(process.cwd(), "packages", "shader-lab", `./node_modules/chevrotain/lib/chevrotain.js`) - }), - ...commonPlugins - ]; + let esFile = path.join(location, pkgJson.module); + let mainFile = path.join(location, pkgJson.main); + if (verboseMode) { + esFile = path.join(location, "dist", "module.verbose.js"); + mainFile = path.join(location, "dist", "main.verbose.js"); + } return { input, external, output: [ { - file: path.join(location, pkgJson.module), + file: esFile, format: "es", sourcemap: true }, { - file: path.join(location, pkgJson.main), + file: mainFile, sourcemap: true, format: "commonjs" } ], - plugins + plugins: curPlugins }; } }; diff --git a/tests/src/shader-lab/Preprocessor.test.ts b/tests/src/shader-lab/Preprocessor.test.ts index 315f857c63..e9a9a7628a 100644 --- a/tests/src/shader-lab/Preprocessor.test.ts +++ b/tests/src/shader-lab/Preprocessor.test.ts @@ -3,7 +3,7 @@ import { testCaseList } from "./test-case"; import { ShaderLib } from "@galacean/engine-core"; import { expect } from "chai"; import { readFileSync } from "fs"; -import { Preprocessor } from "@galacean/engine-shader-lab"; +import { Preprocessor } from "@galacean/engine-shader-lab/verbose"; import { join } from "path"; const includedSource = readFileSync(join(__dirname, "test-case/included.txt")).toString(); diff --git a/tests/src/shader-lab/ShaderLab.test.ts b/tests/src/shader-lab/ShaderLab.test.ts index 1fc6cbfed4..dbb3717f78 100644 --- a/tests/src/shader-lab/ShaderLab.test.ts +++ b/tests/src/shader-lab/ShaderLab.test.ts @@ -1,17 +1,10 @@ -import { - BlendFactor, - BlendOperation, - CompareFunction, - CullMode, - RenderQueueType, - RenderStateDataKey, - StencilOperation -} from "@galacean/engine-core"; +import { BlendOperation, CompareFunction, CullMode, RenderStateDataKey } from "@galacean/engine-core"; import { Color } from "@galacean/engine-math"; -import { ShaderLab } from "@galacean/engine-shader-lab"; -import { glslValidate } from "./ShaderValidate"; +import { ShaderLab as ShaderLabVerbose, GSError } from "@galacean/engine-shader-lab/verbose"; +import { ShaderLab as ShaderLabRelease } from "@galacean/engine-shader-lab"; +import { glslValidate, shaderParse } from "./ShaderValidate"; -import chai, { expect } from "chai"; +import chai, { expect, assert } from "chai"; import spies from "chai-spies"; import fs from "fs"; import path from "path"; @@ -111,7 +104,8 @@ vec4 linearToGamma(vec4 linearIn){ #endif `; -const shaderLab = new ShaderLab(); +const shaderLabVerbose = new ShaderLabVerbose(); +const shaderLabRelease = new ShaderLabRelease(); describe("ShaderLab", () => { let shader: IShaderContent; @@ -120,7 +114,7 @@ describe("ShaderLab", () => { let pass1: IShaderContent["subShaders"][number]["passes"][number]; before(() => { - shader = shaderLab._parseShaderContent(demoShader); + shader = shaderLabVerbose._parseShaderContent(demoShader); subShader = shader.subShaders[0]; passList = subShader.passes; expect(passList[0].isUsePass).to.be.true; @@ -129,7 +123,7 @@ describe("ShaderLab", () => { }); it("create shaderLab", async () => { - expect(shaderLab).not.be.null; + expect(shaderLabVerbose).not.be.null; }); it("shader name", () => { @@ -188,68 +182,80 @@ describe("ShaderLab", () => { }); it("engine shader", async () => { - glslValidate(demoShader, shaderLab); + glslValidate(demoShader, shaderLabVerbose); + glslValidate(demoShader, shaderLabRelease); }); it("include", () => { const demoShader = fs.readFileSync(path.join(__dirname, "shaders/unlit.shader")).toString(); - glslValidate(demoShader, shaderLab, { test_common: commonSource }); + glslValidate(demoShader, shaderLabVerbose, { test_common: commonSource }); }); it("planarShadow shader", () => { const demoShader = fs.readFileSync(path.join(__dirname, "shaders/planarShadow.shader")).toString(); - glslValidate(demoShader, shaderLab); + glslValidate(demoShader, shaderLabVerbose); + glslValidate(demoShader, shaderLabRelease); }); it("Empty macro shader", () => { const demoShader = fs.readFileSync(path.join(__dirname, "shaders/triangle.shader")).toString(); - glslValidate(demoShader, shaderLab); + glslValidate(demoShader, shaderLabVerbose); + glslValidate(demoShader, shaderLabRelease); }); it("No frag shader args", () => { const demoShader = fs.readFileSync(path.join(__dirname, "shaders/noFragArgs.shader")).toString(); - glslValidate(demoShader, shaderLab); + glslValidate(demoShader, shaderLabVerbose); + glslValidate(demoShader, shaderLabRelease); }); it("water full shader(complex)", () => { const demoShader = fs.readFileSync(path.join(__dirname, "shaders/waterfull.shader")).toString(); - glslValidate(demoShader, shaderLab); + glslValidate(demoShader, shaderLabVerbose); + glslValidate(demoShader, shaderLabRelease); }); it("glass shader", () => { const demoShader = fs.readFileSync(path.join(__dirname, "shaders/glass.shader")).toString(); - glslValidate(demoShader, shaderLab); + glslValidate(demoShader, shaderLabVerbose); + glslValidate(demoShader, shaderLabRelease); }); - // it("shader with duplicate name", () => { - // const demoShader = fs.readFileSync(path.join(__dirname, "shaders/glass.shader")).toString(); - // (Shader as any)._shaderLab = shaderLab; - - // const shaderInstance = Shader.create(demoShader); - // expect(shaderInstance).instanceOf(Shader); - - // const errorSpy = chai.spy.on(console, "error"); - // Shader.create(demoShader); - // expect(errorSpy).to.have.been.called.with('Shader named "Gem" already exists.'); - // shaderInstance.destroy(); - // chai.spy.restore(console, "error"); - - // const sameNameShader = Shader.create(demoShader); - // expect(sameNameShader).instanceOf(Shader); - // }); - it("template shader", () => { const demoShader = fs.readFileSync(path.join(__dirname, "shaders/template.shader")).toString(); - glslValidate(demoShader, shaderLab); + glslValidate(demoShader, shaderLabVerbose); + glslValidate(demoShader, shaderLabRelease); }); it("multi-pass", () => { const shaderSource = fs.readFileSync(path.join(__dirname, "shaders/multi-pass.shader")).toString(); - glslValidate(shaderSource, shaderLab); + glslValidate(shaderSource, shaderLabVerbose); + glslValidate(shaderSource, shaderLabRelease); }); it("macro-with-preprocessor", () => { const shaderSource = fs.readFileSync(path.join(__dirname, "shaders/macro-pre.shader")).toString(); - glslValidate(shaderSource, shaderLab); + glslValidate(shaderSource, shaderLabVerbose); + glslValidate(shaderSource, shaderLabRelease); + }); + + it("compilation-error", () => { + const errorShader = fs.readFileSync(path.join(__dirname, "shaders/compilation-error.shader")).toString(); + shaderParse.bind(shaderLabVerbose)(errorShader); + // @ts-ignore + expect(shaderLabVerbose.errors.length).to.eq(3); + // @ts-ignore + assert.instanceOf(shaderLabVerbose.errors[0], GSError); + // @ts-ignore + assert.instanceOf(shaderLabVerbose.errors[1], GSError); + // @ts-ignore + assert.instanceOf(shaderLabVerbose.errors[2], GSError); + + // @ts-ignore + for (const err of shaderLabVerbose.errors) { + console.log(err.toString()); + } + + expect(shaderParse.bind(shaderLabRelease, errorShader)).to.throw(Error); }); }); diff --git a/tests/src/shader-lab/ShaderValidate.ts b/tests/src/shader-lab/ShaderValidate.ts index adf297ed42..82d96fcf70 100644 --- a/tests/src/shader-lab/ShaderValidate.ts +++ b/tests/src/shader-lab/ShaderValidate.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; import { ShaderLab } from "@galacean/engine-shader-lab"; -import { Shader, ShaderFactory, ShaderPass, ShaderPlatformTarget } from "@galacean/engine-core"; +import { Shader, ShaderFactory, ShaderPass, ShaderPlatformTarget, ShaderMacro } from "@galacean/engine-core"; import { IShaderContent } from "@galacean/engine-design/src/shader-lab"; function addLineNum(str: string) { @@ -94,3 +94,30 @@ export function glslValidate(shaderSource, _shaderLab?: ShaderLab, includeMap = }); }); } + +export function shaderParse( + shaderSource: string, + macros: ShaderMacro[] = [], + backend: ShaderPlatformTarget = ShaderPlatformTarget.GLES100 +): (ReturnType & { name: string })[] { + const structInfo = this._parseShaderContent(shaderSource); + const passResult = [] as any; + for (const subShader of structInfo.subShaders) { + for (const pass of subShader.passes) { + if (pass.isUsePass) continue; + const passInfo = this._parseShaderPass( + pass.contents, + pass.vertexEntry, + pass.fragmentEntry, + macros, + backend, + [], + // @ts-ignore + new URL(pass.name, ShaderPass._shaderRootPath).href + ) as any; + passInfo.name = pass.name; + passResult.push(passInfo); + } + } + return passResult; +} diff --git a/tests/src/shader-lab/shaders/compilation-error.shader b/tests/src/shader-lab/shaders/compilation-error.shader new file mode 100644 index 0000000000..09216b6cf4 --- /dev/null +++ b/tests/src/shader-lab/shaders/compilation-error.shader @@ -0,0 +1,100 @@ +Shader "custom/pbr" { + EditorProperties { + material_BaseColor("Main Color", Color) = (0, 0, 0, 1); + material_AlphaCutoff("Alpha Cutoff", Range(0, 1, 0.01)) = 0; + material_BaseTexture("Texture", Texture2D); + } + + EditorMacros { + Header("Conditional Macors") { + MATERIAL_HAS_BASETEXTURE("Base Texture"); + MATERIAL_IS_ALPHA_CUTOFF("Alpha Cutoff"); + MATERIAL_IS_TRANSPARENT("Transparent"); + } + } + + SubShader "Default" { + Pass "Pass0" { + + #ifdef MATERIAL_IS_TRANSPARENT + BlendState { + Enabled = true; + SourceColorBlendFactor = BlendFactor.SourceAlpha; + DestinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha; + SourceAlphaBlendFactor = BlendFactor.One; + DestinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha; + } + DepthState { + WriteEnabled = false; + } + RenderQueueType = Transparent; + #else + BlendState { + Enabled = false; + SourceColorBlendFactor = BlendFactor.SourceAlpha; + DestinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha; + SourceAlphaBlendFactor = BlendFactor.One; + DestinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha; + } + DepthState { + WriteEnabled = true; + } + RenderQueueType = Opaque; + #endif + + mat4 renderer_MVPMat; + vec4 material_BaseColor; + float material_AlphaCutoff; + sampler2D material_BaseTexture; + + struct Attributes { + vec4 POSITION; + vec2 TEXCOORD_0; + }; + + struct Varyings { + vec3 v_pos; + vec2 v_uv; + }; + + VertexShader = vert; + FragmentShader = frag; + + Varyings vert(Attributes attr) { + Varyings v; + + gl_Position = renderer_MVPMat * attr2.POSITION; + none( + 12 + ); + v.v_pos = gl_Position.xyz; + v.v_uv = attr.TEXCOORD_023; + return v; + } + + void frag(Varyings v) { + vec4 baseColor = material_BaseColor; + + #ifdef MATERIAL_HAS_BASETEXTURE + vec4 textureColor = texture2D(material_BaseTexture, v.v_uv); + #ifndef ENGINE_IS_COLORSPACE_GAMMA + textureColor = gammaToLinear(textureColor); + #endif + baseColor *= textureColor; + #endif + + #ifdef MATERIAL_IS_ALPHA_CUTOFF + if( baseColor.a < material_AlphaCutoff ) { + discard; + } + #endif + + gl_FragColor = baseColor; + + #ifndef MATERIAL_IS_TRANSPARENT + gl_FragColor.a = 1.0; + #endif + } + } + } + } \ No newline at end of file diff --git a/tests/src/shader-lab/shaders/demo.shader b/tests/src/shader-lab/shaders/demo.shader index 3f1d37a88d..c6ca75e4f2 100644 --- a/tests/src/shader-lab/shaders/demo.shader +++ b/tests/src/shader-lab/shaders/demo.shader @@ -75,8 +75,13 @@ Shader "Water" { RenderQueueType = Opaque; + /* First comment */ + /* Second comment */ + #define SCENE_SHADOW_TYPE 3 + /*Comment without leading space*/ + v2f vert(a2v v) { v2f o; @@ -88,6 +93,9 @@ Shader "Water" { return o; } + /* This is a + multi-line comment */ + void frag(v2f i) { vec4 color = texture2D(material_BaseTexture, i.v_uv) * u_color; float fogDistance = length(i.v_position); diff --git a/tests/src/shader-lab/shaders/multi-pass.shader b/tests/src/shader-lab/shaders/multi-pass.shader index 2e7314ee81..bfb68f50fd 100644 --- a/tests/src/shader-lab/shaders/multi-pass.shader +++ b/tests/src/shader-lab/shaders/multi-pass.shader @@ -2,6 +2,14 @@ Shader "Triangle" { SubShader "Default" { mat4 renderer_MVPMat; + BlendState transparentBlendState { + Enabled = true; + SourceColorBlendFactor = BlendFactor.SourceAlpha; + DestinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha; + SourceAlphaBlendFactor = BlendFactor.One; + DestinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha; + } + struct a2v { vec4 POSITION; vec2 TEXCOORD_0; @@ -52,6 +60,8 @@ Shader "Triangle" { Pass "0" { vec3 u_color; + BlendState = transparentBlendState; + struct a2v { vec4 POSITION; }; @@ -79,6 +89,8 @@ Shader "Triangle" { Pass "1" { vec3 u_color; + BlendState = transparentBlendState; + struct a2v { vec4 POSITION; }; diff --git a/tests/src/shader-lab/test-case/source/frag.txt b/tests/src/shader-lab/test-case/source/frag.txt index c0ee4f0cea..e697d6028d 100644 --- a/tests/src/shader-lab/test-case/source/frag.txt +++ b/tests/src/shader-lab/test-case/source/frag.txt @@ -10,7 +10,8 @@ void Gerstner() { // #include "ShadowCoord" -#define TT 1.0 // comments +#define TT 1./** comments +*/0 // test #define QQ vec4 a = vec4(TT,1.0,3.0,4.0);