From 07186da9e363c246049b7f3f6b51f8d6d0ccc34d Mon Sep 17 00:00:00 2001 From: SwayYan Date: Tue, 24 Dec 2024 18:20:44 +0800 Subject: [PATCH] ShaderLab support MRT (#2452) * feat: add mrt syntax --- e2e/case/shaderLab-mrt.ts | 113 +++ e2e/config.ts | 5 + .../originImage/Material_shaderLab-mrt.jpg | 3 + packages/shader-lab/src/GSError.ts | 3 +- packages/shader-lab/src/ParserUtils.ts | 8 +- packages/shader-lab/src/ShaderLabUtils.ts | 6 +- .../shader-lab/src/codeGen/CodeGenVisitor.ts | 78 +- packages/shader-lab/src/codeGen/GLES100.ts | 50 +- packages/shader-lab/src/codeGen/GLES300.ts | 98 ++- .../shader-lab/src/codeGen/GLESVisitor.ts | 65 +- .../shader-lab/src/codeGen/VisitorContext.ts | 38 +- packages/shader-lab/src/common/BaseScanner.ts | 2 +- .../shader-lab/src/common/BaseSymbolTable.ts | 44 +- packages/shader-lab/src/common/Keywords.ts | 3 + packages/shader-lab/src/common/types.ts | 5 +- .../src/contentParser/ContentSymbolTable.ts | 33 + .../src/contentParser/ShaderContentParser.ts | 47 +- .../src/contentParser/SymbolTable.ts | 12 - packages/shader-lab/src/lalr/CFG.ts | 472 ++++++------ packages/shader-lab/src/lalr/LALR1.ts | 22 +- packages/shader-lab/src/lalr/Production.ts | 6 +- packages/shader-lab/src/lalr/StateItem.ts | 4 +- packages/shader-lab/src/lalr/Utils.ts | 21 +- packages/shader-lab/src/lalr/types.ts | 4 +- packages/shader-lab/src/lexer/Lexer.ts | 1 - packages/shader-lab/src/parser/AST.ts | 696 +++++------------- packages/shader-lab/src/parser/Grammar.ts | 16 +- .../shader-lab/src/parser/GrammarSymbol.ts | 5 +- .../shader-lab/src/parser/SemanticAnalyzer.ts | 49 +- packages/shader-lab/src/parser/ShaderInfo.ts | 4 +- .../src/parser/ShaderTargetParser.ts | 4 +- packages/shader-lab/src/parser/TargetParser.y | 8 +- .../src/parser/builtin/variables.ts | 3 +- .../src/parser/symbolTable/SymbolInfo.ts | 6 +- .../src/parser/symbolTable/SymbolTable.ts | 29 - .../parser/symbolTable/TargetSymbolTable.ts | 74 ++ .../src/parser/symbolTable/index.ts | 2 +- packages/shader-lab/src/parser/types.ts | 33 +- tests/src/shader-lab/ShaderLab.test.ts | 29 +- .../src/shader-lab/shaders/mrt-error1.shader | 37 + .../src/shader-lab/shaders/mrt-normal.shader | 42 ++ .../src/shader-lab/shaders/mrt-struct.shader | 50 ++ tests/vitest.config.ts | 3 +- 43 files changed, 1224 insertions(+), 1009 deletions(-) create mode 100644 e2e/case/shaderLab-mrt.ts create mode 100644 e2e/fixtures/originImage/Material_shaderLab-mrt.jpg create mode 100644 packages/shader-lab/src/contentParser/ContentSymbolTable.ts delete mode 100644 packages/shader-lab/src/contentParser/SymbolTable.ts delete mode 100644 packages/shader-lab/src/parser/symbolTable/SymbolTable.ts create mode 100644 packages/shader-lab/src/parser/symbolTable/TargetSymbolTable.ts create mode 100644 tests/src/shader-lab/shaders/mrt-error1.shader create mode 100644 tests/src/shader-lab/shaders/mrt-normal.shader create mode 100644 tests/src/shader-lab/shaders/mrt-struct.shader diff --git a/e2e/case/shaderLab-mrt.ts b/e2e/case/shaderLab-mrt.ts new file mode 100644 index 0000000000..c46e78b133 --- /dev/null +++ b/e2e/case/shaderLab-mrt.ts @@ -0,0 +1,113 @@ +/** + * @title ShaderLab MRT + * @category Material + */ + +import { Camera, Color, Logger, Material, MeshRenderer, PrimitiveMesh, Shader, WebGLEngine } from "@galacean/engine"; +import { ShaderLab } from "@galacean/engine-shader-lab"; +import { initScreenshot, updateForE2E } from "./.mockForE2E"; + +const shaderLab = new ShaderLab(); + +const shaderSource = `Shader "/custom.gs" { + SubShader "Default" { + UsePass "pbr/Default/ShadowCaster" + + Pass "Pass0" { + struct Attributes { + vec3 POSITION; + vec2 TEXCOORD_0; + vec4 JOINTS_0; + vec4 WEIGHTS_0; + }; + + struct Varyings { + vec2 uv; + }; + + mat4 renderer_MVPMat; + + vec4 material_BaseColor; + + + VertexShader = vert; + FragmentShader = frag; + + Varyings vert(Attributes attr) { + Varyings v; + + vec4 position = vec4(attr.POSITION, 1.0); + + // Skin + #ifdef RENDERER_HAS_SKIN + mat4 skinMatrix = getSkinMatrix(attr); + position = skinMatrix * position; + #endif + + gl_Position = renderer_MVPMat * position; + v.uv = attr.TEXCOORD_0; + + return v; + } + + struct mrt { + layout(location = 0) vec4 fragColor0; + layout(location = 1) vec4 fragColor1; + }; + + mrt frag(Varyings v) { + mrt o; + + vec4 baseColor = material_BaseColor; + + #ifdef MATERIAL_HAS_BASETEXTURE + vec4 textureColor = texture2D(material_BaseTexture, 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 + + o.fragColor0 = baseColor; + o.fragColor1 = baseColor; + + return o; + } + } + } + }`; + +Logger.enable(); +WebGLEngine.create({ canvas: "canvas", shaderLab }).then((engine) => { + engine.canvas.resizeByClientSize(); + + const shader = Shader.create(shaderSource); + const scene = engine.sceneManager.activeScene; + const rootEntity = scene.createRootEntity(); + + // camera + const cameraEntity = rootEntity.createChild("cameraNode"); + cameraEntity.transform.setPosition(0, 0, 5); + const camera = cameraEntity.addComponent(Camera); + + // sphere + { + const sphere = rootEntity.createChild("sphere"); + sphere.transform.position.x = -1; + const renderer = sphere.addComponent(MeshRenderer); + renderer.mesh = PrimitiveMesh.createSphere(engine); + const material = new Material(engine, shader); + material.shaderData.setColor("material_BaseColor", new Color(1, 0, 0, 0.2)); + renderer.setMaterial(material); + } + + updateForE2E(engine); + + initScreenshot(engine, camera); +}); diff --git a/e2e/config.ts b/e2e/config.ts index 5aabc5b725..8c342d813f 100644 --- a/e2e/config.ts +++ b/e2e/config.ts @@ -110,6 +110,11 @@ export const E2E_CONFIG = { caseFileName: "material-shaderLab", threshold: 0.2 }, + shaderLabMRT: { + category: "Material", + caseFileName: "shaderLab-mrt", + threshold: 0.2 + }, shaderReplacement: { category: "Material", caseFileName: "material-shaderReplacement", diff --git a/e2e/fixtures/originImage/Material_shaderLab-mrt.jpg b/e2e/fixtures/originImage/Material_shaderLab-mrt.jpg new file mode 100644 index 0000000000..4621abc35a --- /dev/null +++ b/e2e/fixtures/originImage/Material_shaderLab-mrt.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:233b89b0d3bf85389142f721bc072d86f4efcc42b8df9567efb6ccd041269c70 +size 23846 diff --git a/packages/shader-lab/src/GSError.ts b/packages/shader-lab/src/GSError.ts index cbce38d1f4..9073591300 100644 --- a/packages/shader-lab/src/GSError.ts +++ b/packages/shader-lab/src/GSError.ts @@ -47,7 +47,8 @@ export class GSError extends Error { if (i === start.line) { remarkStart = start.column; paddingLength += start.column; - } else if (i === end.line) { + } + if (i === end.line) { remarkEnd = end.column; } const remarkLength = Math.max(remarkEnd - remarkStart, 1); diff --git a/packages/shader-lab/src/ParserUtils.ts b/packages/shader-lab/src/ParserUtils.ts index a269ef3199..03e93ab4a9 100644 --- a/packages/shader-lab/src/ParserUtils.ts +++ b/packages/shader-lab/src/ParserUtils.ts @@ -1,4 +1,4 @@ -import { ENonTerminal, GrammarSymbol } from "./parser/GrammarSymbol"; +import { NoneTerminal, GrammarSymbol } from "./parser/GrammarSymbol"; import { BaseToken as Token } from "./common/BaseToken"; import { EKeyword, ETokenType, GalaceanDataType } from "./common"; import { TreeNode } from "./parser/AST"; @@ -7,7 +7,7 @@ import State from "./lalr/State"; // #endif export class ParserUtils { - static unwrapNodeByType(node: TreeNode, type: ENonTerminal): T | undefined { + static unwrapNodeByType(node: TreeNode, type: NoneTerminal): T | undefined { const child = node.children[0]; if (child instanceof Token) return; if (child.nt === type) return child as T; @@ -30,12 +30,12 @@ export class ParserUtils { if (this.isTerminal(sm)) { return ETokenType[sm] ?? EKeyword[sm]; } - return ENonTerminal[sm]; + return NoneTerminal[sm]; } // #endif static isTerminal(sm: GrammarSymbol) { - return sm < ENonTerminal.START; + return sm < NoneTerminal.START; } /** diff --git a/packages/shader-lab/src/ShaderLabUtils.ts b/packages/shader-lab/src/ShaderLabUtils.ts index f4d411ef13..230e190494 100644 --- a/packages/shader-lab/src/ShaderLabUtils.ts +++ b/packages/shader-lab/src/ShaderLabUtils.ts @@ -1,4 +1,4 @@ -import { ClearableObjectPool, IPoolElement } from "@galacean/engine"; +import { ClearableObjectPool, IPoolElement, Logger } from "@galacean/engine"; import { GSErrorName } from "./GSError"; import { ShaderRange } from "./common/ShaderRange"; import { ShaderPosition } from "./common/ShaderPosition"; @@ -27,11 +27,11 @@ export class ShaderLabUtils { source: string, location: ShaderRange | ShaderPosition, file?: string - ): Error { + ): Error | undefined { // #if _VERBOSE return new GSError(errorName, message, location, source, file); // #else - return new Error(`[${errorName}]: ${message}`); + Logger.error(message); // #endif } } diff --git a/packages/shader-lab/src/codeGen/CodeGenVisitor.ts b/packages/shader-lab/src/codeGen/CodeGenVisitor.ts index cd696e61d5..fe940e1b1b 100644 --- a/packages/shader-lab/src/codeGen/CodeGenVisitor.ts +++ b/packages/shader-lab/src/codeGen/CodeGenVisitor.ts @@ -1,4 +1,4 @@ -import { ENonTerminal } from "../parser/GrammarSymbol"; +import { NoneTerminal } from "../parser/GrammarSymbol"; import { BaseToken as Token } from "../common/BaseToken"; import { EKeyword, ShaderPosition, ShaderRange } from "../common"; import { ASTNode, TreeNode } from "../parser/AST"; @@ -11,10 +11,12 @@ import { GSErrorName } from "../GSError"; // #if _VERBOSE import { GSError } from "../GSError"; // #endif -import { ShaderLabUtils } from "../ShaderLabUtils"; import { Logger, ReturnableObjectPool } from "@galacean/engine"; import { TempArray } from "../TempArray"; +export const V3_GL_FragColor = "GS_glFragColor"; +export const V3_GL_FragData = "GS_glFragData"; + /** * @internal * The code generator @@ -23,6 +25,10 @@ export abstract class CodeGenVisitor { // #if _VERBOSE readonly errors: Error[] = []; // #endif + + abstract getFragDataCodeGen(index: string | number): string; + abstract getReferencedMRTPropText(index: string | number, ident: string): string; + protected static _tmpArrayPool = new ReturnableObjectPool(TempArray, 10); defaultCodeGen(children: NodeChild[]) { @@ -41,12 +47,13 @@ export abstract class CodeGenVisitor { } visitPostfixExpression(node: ASTNode.PostfixExpression) { - if (node.children.length === 3) { - const context = VisitorContext.context; - - const postExpr = node.children[0] as ASTNode.PostfixExpression; + const children = node.children; + const derivationLength = children.length; + const context = VisitorContext.context; - const prop = node.children[2]; + if (derivationLength === 3) { + const postExpr = children[0] as ASTNode.PostfixExpression; + const prop = children[2]; if (prop instanceof Token) { if (context.isAttributeStruct(postExpr.type)) { @@ -65,12 +72,36 @@ export abstract class CodeGenVisitor { } // #endif return prop.lexeme; + } else if (context.isMRTStruct(postExpr.type)) { + const error = context.referenceMRTProp(prop); + // #if _VERBOSE + if (error) { + this.errors.push(error); + } + // #endif + return prop.lexeme; } return `${postExpr.codeGen(this)}.${prop.lexeme}`; } else { return `${postExpr.codeGen(this)}.${prop.codeGen(this)}`; } + } else if (derivationLength === 4) { + const identNode = children[0] as ASTNode.PostfixExpression; + const indexNode = children[2] as ASTNode.Expression; + const identLexeme = identNode.codeGen(this); + const indexLexeme = indexNode.codeGen(this); + if (identLexeme === "gl_FragData") { + // #if _VERBOSE + if (context._referencedVaryingList[V3_GL_FragColor]) { + this._reportError(identNode.location, "cannot use both gl_FragData and gl_FragColor"); + } + // #endif + const mrtLexeme = this.getFragDataCodeGen(indexLexeme); + context._referencedMRTList[mrtLexeme] = this.getReferencedMRTPropText(indexLexeme, mrtLexeme); + return mrtLexeme; + } + return `${identLexeme}[${indexLexeme}]`; } return this.defaultCodeGen(node.children); } @@ -110,7 +141,7 @@ export abstract class CodeGenVisitor { visitStatementList(node: ASTNode.StatementList): string { const children = node.children as TreeNode[]; - if (node.children.length === 1) { + if (children.length === 1) { return children[0].codeGen(this); } else { return `${children[0].codeGen(this)}\n${children[1].codeGen(this)}`; @@ -126,22 +157,24 @@ export abstract class CodeGenVisitor { } visitGlobalVariableDeclaration(node: ASTNode.VariableDeclaration): string { - const fullType = node.children[0]; + const children = node.children; + const fullType = children[0]; if (fullType instanceof ASTNode.FullySpecifiedType && fullType.typeSpecifier.isCustom) { VisitorContext.context.referenceGlobal(fullType.type, ESymbolType.STRUCT); } - return this.defaultCodeGen(node.children); + return this.defaultCodeGen(children); } visitDeclaration(node: ASTNode.Declaration): string { - const child = node.children[0]; - if ( - child instanceof ASTNode.InitDeclaratorList && - child.typeInfo.typeLexeme === VisitorContext.context.varyingStruct?.ident?.lexeme - ) { - return ""; + const { context } = VisitorContext; + const children = node.children; + const child = children[0]; + + if (child instanceof ASTNode.InitDeclaratorList) { + const typeLexeme = child.typeInfo.typeLexeme; + if (context.isVaryingStruct(typeLexeme) || context.isMRTStruct(typeLexeme)) return ""; } - return this.defaultCodeGen(node.children); + return this.defaultCodeGen(children); } visitFunctionProtoType(node: ASTNode.FunctionProtoType): string { @@ -174,24 +207,25 @@ export abstract class CodeGenVisitor { } visitJumpStatement(node: ASTNode.JumpStatement): string { - const cmd = node.children[0] as Token; + const children = node.children; + const cmd = children[0] as Token; if (cmd.type === EKeyword.RETURN) { - const expr = node.children[1]; + const expr = children[1]; if (expr instanceof ASTNode.Expression) { const returnVar = ParserUtils.unwrapNodeByType( expr, - ENonTerminal.variable_identifier + NoneTerminal.variable_identifier ); if (returnVar?.typeInfo === VisitorContext.context.varyingStruct?.ident?.lexeme) { return ""; } - const returnFnCall = ParserUtils.unwrapNodeByType(expr, ENonTerminal.function_call); + const returnFnCall = ParserUtils.unwrapNodeByType(expr, NoneTerminal.function_call); if (returnFnCall?.type === VisitorContext.context.varyingStruct?.ident?.lexeme) { return `${expr.codeGen(this)};`; } } } - return this.defaultCodeGen(node.children); + return this.defaultCodeGen(children); } visitFunctionIdentifier(node: ASTNode.FunctionIdentifier): string { diff --git a/packages/shader-lab/src/codeGen/GLES100.ts b/packages/shader-lab/src/codeGen/GLES100.ts index fdfd225f71..9243493363 100644 --- a/packages/shader-lab/src/codeGen/GLES100.ts +++ b/packages/shader-lab/src/codeGen/GLES100.ts @@ -1,3 +1,5 @@ +import { BaseToken } from "../common/BaseToken"; +import { ASTNode } from "../parser/AST"; import { GLESVisitor } from "./GLESVisitor"; import { VisitorContext } from "./VisitorContext"; import { ICodeSegment } from "./types"; @@ -21,25 +23,57 @@ export class GLES100Visitor extends GLESVisitor { return this._singleton; } - override getAttributeDeclare(): ICodeSegment[] { - const ret: ICodeSegment[] = []; + override getFragDataCodeGen(index: string | number): string { + return `gl_FragData[${index}]`; + } + + override getReferencedMRTPropText(index: string | number, ident: string): string { + return ""; + } + + override getAttributeDeclare(out: ICodeSegment[]): void { for (const item of Object.values(VisitorContext.context._referencedAttributeList)) { - ret.push({ + out.push({ text: `attribute ${item.typeInfo.typeLexeme} ${item.ident.lexeme};`, index: item.ident.location.start.index }); } - return ret; } - override getVaryingDeclare(): ICodeSegment[] { - const ret: ICodeSegment[] = []; + override getVaryingDeclare(out: ICodeSegment[]): void { for (const item of Object.values(VisitorContext.context._referencedVaryingList)) { - ret.push({ + out.push({ text: `varying ${item.typeInfo.typeLexeme} ${item.ident.lexeme};`, index: item.ident.location.start.index }); } - return ret; + } + + override getMRTDeclare(out: ICodeSegment[]): void { + return; + } + + override visitPostfixExpression(node: ASTNode.PostfixExpression): string { + const { children } = node; + const postExpr = children[0]; + const { context } = VisitorContext; + if (postExpr instanceof ASTNode.PostfixExpression && context.isMRTStruct(postExpr.type)) { + const propReferenced = children[2] as BaseToken; + const prop = context.mrtStruct!.propList.find((item) => item.ident.lexeme === propReferenced.lexeme); + if (!prop) { + this._reportError(propReferenced.location, `not found mrt property: ${propReferenced.lexeme}`); + return ""; + } + return `gl_FragData[${prop.mrtIndex!}]`; + } + return super.visitPostfixExpression(node); + } + + override visitJumpStatement(node: ASTNode.JumpStatement): string { + if (node.isFragReturnStatement) { + const expression = node.children[1] as ASTNode.Expression; + return `gl_FragColor = ${expression.codeGen(this)}`; + } + return super.visitJumpStatement(node); } } diff --git a/packages/shader-lab/src/codeGen/GLES300.ts b/packages/shader-lab/src/codeGen/GLES300.ts index 31576ab866..536a594706 100644 --- a/packages/shader-lab/src/codeGen/GLES300.ts +++ b/packages/shader-lab/src/codeGen/GLES300.ts @@ -1,14 +1,13 @@ -import { ASTNode } from "../parser/AST"; +import { ASTNode, TreeNode } from "../parser/AST"; import { SymbolType } from "../parser/types"; import { BaseToken as Token } from "../common/BaseToken"; -import { EKeyword, ETokenType, ShaderPosition } from "../common"; +import { EKeyword, ETokenType } from "../common"; import { GLESVisitor } from "./GLESVisitor"; import { EShaderStage } from "../common/Enums"; import { ICodeSegment } from "./types"; import { VisitorContext } from "./VisitorContext"; import { ShaderLab } from "../ShaderLab"; - -const V3_GL_FragColor = "GS_glFragColor"; +import { V3_GL_FragColor, V3_GL_FragData } from "./CodeGenVisitor"; export class GLES300Visitor extends GLESVisitor { override _versionText: string = "#version 300 es"; @@ -21,35 +20,58 @@ export class GLES300Visitor extends GLESVisitor { return this._singleton; } - override getAttributeDeclare(): ICodeSegment[] { - const ret: ICodeSegment[] = []; + override getFragDataCodeGen(index: string | number): string { + return `${V3_GL_FragData}_${index}`; + } + + override getReferencedMRTPropText(index: string | number, ident: string): string { + return `layout(location = ${index}) out vec4 ${ident};`; + } + + override getAttributeDeclare(out: ICodeSegment[]): void { for (const item of Object.values(VisitorContext.context._referencedAttributeList)) { - ret.push({ + out.push({ text: `in ${item.typeInfo.typeLexeme} ${item.ident.lexeme};`, index: item.ident.location.start.index }); } - return ret; } - override getVaryingDeclare(): ICodeSegment[] { - const ret: ICodeSegment[] = []; + override getVaryingDeclare(out: ICodeSegment[]): void { const qualifier = VisitorContext.context.stage === EShaderStage.FRAGMENT ? "in" : "out"; const values = Object.values(VisitorContext.context._referencedVaryingList); for (let i = 0; i < values.length; i++) { const item = values[i]; - ret.push({ + out.push({ text: `${item.qualifier ?? qualifier} ${item.typeInfo.typeLexeme} ${item.ident.lexeme};`, index: item.ident.location.start.index }); } - return ret; + } + + override getMRTDeclare(out: ICodeSegment[]): void { + const referencedMRTList = VisitorContext.context._referencedMRTList; + for (let ident in referencedMRTList) { + const info = referencedMRTList[ident]; + if (typeof info === "string") { + out.push({ + text: info, + index: Number.MAX_SAFE_INTEGER + }); + } else { + out.push({ + text: this.getReferencedMRTPropText(info.mrtIndex, ident), + index: info.ident.location.start.index + }); + } + } } override visitFunctionIdentifier(node: ASTNode.FunctionIdentifier): string { - const typeSpecifier = node.children[0] as ASTNode.TypeSpecifier; + const children = node.children; + const typeSpecifier = children[0] as ASTNode.TypeSpecifier; if (typeSpecifier.children.length !== 1) { - return this.defaultCodeGen(node.children); + return this.defaultCodeGen(children); } let ident = node.lexeme; if (node.ident === "texture2D" || node.ident === "textureCube") { @@ -81,19 +103,47 @@ export class GLES300Visitor extends GLESVisitor { } override visitVariableIdentifier(node: ASTNode.VariableIdentifier): string { - if (VisitorContext.context.stage === EShaderStage.FRAGMENT && node.lexeme === "gl_FragColor") { - if (!VisitorContext.context._referencedVaryingList[V3_GL_FragColor]) { - const token = Token.pool.get(); - token.set(ETokenType.ID, V3_GL_FragColor, ShaderLab.createPosition(0, 0, 0)); - VisitorContext.context._referencedVaryingList[V3_GL_FragColor] = { - ident: token, - typeInfo: new SymbolType(EKeyword.VEC4, "vec4"), - qualifier: "out", - astNode: node - }; + const { context } = VisitorContext; + if (context.stage === EShaderStage.FRAGMENT && node.lexeme === "gl_FragColor") { + // #if _VERBOSE + if (context._referencedMRTList["gl_FragData"]) { + this._reportError(node.location, "cannot use both gl_FragData and gl_FragColor"); } + if (context.mrtStruct) { + this._reportError(node.location, "gl_FragColor cannot be used with MRT (Multiple Render Targets)."); + } + // #endif + this._registerFragColorVariable(node); return V3_GL_FragColor; } return super.visitVariableIdentifier(node); } + + override visitJumpStatement(node: ASTNode.JumpStatement): string { + if (node.isFragReturnStatement) { + const { mrtStruct } = VisitorContext.context; + if (mrtStruct) { + return ""; + } + this._registerFragColorVariable(node); + + const expression = node.children[1] as ASTNode.Expression; + return `${V3_GL_FragColor} = ${expression.codeGen(this)};`; + } + return super.visitJumpStatement(node); + } + + private _registerFragColorVariable(node: TreeNode) { + const { _referencedVaryingList } = VisitorContext.context; + if (!_referencedVaryingList[V3_GL_FragColor]) { + const token = Token.pool.get(); + token.set(ETokenType.ID, V3_GL_FragColor, ShaderLab.createPosition(0, 0, 0)); + _referencedVaryingList[V3_GL_FragColor] = { + ident: token, + typeInfo: new SymbolType(EKeyword.VEC4, "vec4"), + qualifier: "out", + astNode: node + }; + } + } } diff --git a/packages/shader-lab/src/codeGen/GLESVisitor.ts b/packages/shader-lab/src/codeGen/GLESVisitor.ts index e3923dcf3e..e3a771992e 100644 --- a/packages/shader-lab/src/codeGen/GLESVisitor.ts +++ b/packages/shader-lab/src/codeGen/GLESVisitor.ts @@ -24,9 +24,11 @@ const defaultPrecision = ` export abstract class GLESVisitor extends CodeGenVisitor { protected _versionText: string = ""; protected _extensions: string = ""; + private _globalCodeArray: ICodeSegment[] = []; - abstract getAttributeDeclare(): ICodeSegment[]; - abstract getVaryingDeclare(): ICodeSegment[]; + abstract getAttributeDeclare(out: ICodeSegment[]): void; + abstract getVaryingDeclare(out: ICodeSegment[]): void; + abstract getMRTDeclare(out: ICodeSegment[]): void; visitShaderProgram(node: ASTNode.GLShaderProgram, vertexEntry: string, fragmentEntry: string): IShaderInfo { // #if _VERBOSE @@ -43,7 +45,7 @@ export abstract class GLESVisitor extends CodeGenVisitor { vertexMain(entry: string, data: ShaderData): string { const { symbolTable } = data; - const fnSymbol = symbolTable.lookup({ ident: entry, symbolType: ESymbolType.FN }); + const fnSymbol = symbolTable.lookup(entry, ESymbolType.FN); if (!fnSymbol?.astNode) throw `no entry function found: ${entry}`; const fnNode = fnSymbol.astNode; @@ -51,24 +53,21 @@ export abstract class GLESVisitor extends CodeGenVisitor { const returnType = fnNode.protoType.returnType; if (typeof returnType.type === "string") { - const varyStruct = symbolTable.lookup({ ident: returnType.type, symbolType: ESymbolType.STRUCT }); + const varyStruct = symbolTable.lookup(returnType.type, ESymbolType.STRUCT); if (!varyStruct) { 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."); + this._reportError(returnType.location, "vertex main entry can only return struct or void."); } const paramList = fnNode.protoType.parameterList; if (paramList?.length) { for (const paramInfo of paramList) { if (typeof paramInfo.typeInfo.type === "string") { - const structSymbol = symbolTable.lookup({ - ident: paramInfo.typeInfo.type, - symbolType: ESymbolType.STRUCT - }); + const structSymbol = symbolTable.lookup(paramInfo.typeInfo.type, ESymbolType.STRUCT); if (!structSymbol) { this._reportError(paramInfo.astNode.location, `Not found attribute struct "${paramInfo.typeInfo.type}".`); continue; @@ -84,12 +83,15 @@ export abstract class GLESVisitor extends CodeGenVisitor { } const statements = fnNode.statements.codeGen(this); - const globalText = this._getGlobalText(data); - const attributeDeclare = this.getAttributeDeclare(); - const varyingDeclare = this.getVaryingDeclare(); + const { _globalCodeArray: globalCodeArray } = this; + globalCodeArray.length = 0; - const globalCode = [...globalText, ...attributeDeclare, ...varyingDeclare] + this._getGlobalText(data, globalCodeArray); + this.getAttributeDeclare(globalCodeArray); + this.getVaryingDeclare(globalCodeArray); + + const globalCode = globalCodeArray .sort((a, b) => a.index - b.index) .map((item) => item.text) .join("\n"); @@ -101,27 +103,50 @@ export abstract class GLESVisitor extends CodeGenVisitor { private _fragmentMain(entry: string, data: ShaderData): string { const { symbolTable } = data; - const fnSymbol = symbolTable.lookup({ ident: entry, symbolType: ESymbolType.FN }); + const fnSymbol = symbolTable.lookup(entry, ESymbolType.FN); if (!fnSymbol?.astNode) throw `no entry function found: ${entry}`; const fnNode = fnSymbol.astNode; - VisitorContext.context.stage = EShaderStage.FRAGMENT; + const { returnStatement } = fnNode; + if (returnStatement) { + returnStatement.isFragReturnStatement = true; + } + + const { context } = VisitorContext; + context.stage = EShaderStage.FRAGMENT; + + const { type: returnDataType, location: returnLocation } = fnNode.protoType.returnType; + if (typeof returnDataType === "string") { + const mrtStruct = symbolTable.lookup(returnDataType, ESymbolType.STRUCT); + if (!mrtStruct) { + this._reportError(returnLocation, `invalid mrt struct: ${returnDataType}`); + } else { + context.mrtStruct = mrtStruct.astNode; + } + } else if (returnDataType !== EKeyword.VOID && returnDataType !== EKeyword.VEC4) { + this._reportError(returnLocation, "fragment main entry can only return struct or vec4."); + } + const statements = fnNode.statements.codeGen(this); - const globalText = this._getGlobalText(data); - const varyingDeclare = this.getVaryingDeclare(); + const { _globalCodeArray: globalCodeArray } = this; + globalCodeArray.length = 0; - const globalCode = [...globalText, ...varyingDeclare] + this._getGlobalText(data, globalCodeArray); + this.getVaryingDeclare(globalCodeArray); + this.getMRTDeclare(globalCodeArray); + + const globalCode = globalCodeArray .sort((a, b) => a.index - b.index) .map((item) => item.text) .join("\n"); - VisitorContext.context.reset(); + context.reset(); return `${this._versionText}\n${this._extensions}\n${defaultPrecision}\n${globalCode}\n\nvoid main() ${statements}`; } private _getGlobalText( data: ShaderData, - textList: ICodeSegment[] = [], + textList: ICodeSegment[], lastLength: number = 0, _serialized: Set = new Set() ): ICodeSegment[] { diff --git a/packages/shader-lab/src/codeGen/VisitorContext.ts b/packages/shader-lab/src/codeGen/VisitorContext.ts index 6a177d12b7..5a91e97076 100644 --- a/packages/shader-lab/src/codeGen/VisitorContext.ts +++ b/packages/shader-lab/src/codeGen/VisitorContext.ts @@ -1,14 +1,11 @@ import { EShaderStage } from "../common/Enums"; import { ASTNode } from "../parser/AST"; -import { ESymbolType, SymbolTable, SymbolInfo } from "../parser/symbolTable"; -import { IParamInfo } from "../parser/types"; +import { ESymbolType, TargetSymbolTable, SymbolInfo } from "../parser/symbolTable"; +import { IParamInfo, StructProp } 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 { @@ -27,16 +24,18 @@ export class VisitorContext { attributeList: IParamInfo[] = []; attributeStructs: ASTNode.StructSpecifier[] = []; varyingStruct?: ASTNode.StructSpecifier; + mrtStruct?: ASTNode.StructSpecifier; stage: EShaderStage; _referencedAttributeList: Record = Object.create(null); _referencedGlobals: Record = Object.create(null); _referencedVaryingList: Record = Object.create(null); + _referencedMRTList: Record = Object.create(null); _curFn?: ASTNode.FunctionProtoType; - _passSymbolTable: SymbolTable; + _passSymbolTable: TargetSymbolTable; private constructor() {} @@ -50,6 +49,8 @@ export class VisitorContext { this._referencedAttributeList = Object.create(null); this._referencedGlobals = Object.create(null); this._referencedVaryingList = Object.create(null); + this._referencedMRTList = Object.create(null); + this.mrtStruct = undefined; } isAttributeStruct(type: string) { @@ -60,7 +61,11 @@ export class VisitorContext { return this.varyingStruct?.ident?.lexeme === type; } - referenceAttribute(ident: BaseToken): Error { + isMRTStruct(type: string) { + return this.mrtStruct?.ident?.lexeme === type; + } + + referenceAttribute(ident: BaseToken): Error | void { if (this._referencedAttributeList[ident.lexeme]) return; const prop = this.attributeList.find((item) => item.ident.lexeme === ident.lexeme); @@ -75,7 +80,7 @@ export class VisitorContext { this._referencedAttributeList[ident.lexeme] = prop; } - referenceVarying(ident: BaseToken): Error | undefined { + referenceVarying(ident: BaseToken): Error | void { if (this._referencedVaryingList[ident.lexeme]) return; const prop = this.varyingStruct?.propList.find((item) => item.ident.lexeme === ident.lexeme); @@ -90,6 +95,21 @@ export class VisitorContext { this._referencedVaryingList[ident.lexeme] = prop; } + referenceMRTProp(ident: BaseToken): Error | void { + if (this._referencedMRTList[ident.lexeme]) return; + + const prop = this.mrtStruct?.propList.find((item) => item.ident.lexeme === ident.lexeme); + if (!prop) { + return ShaderLabUtils.createGSError( + `referenced mrt not found: ${ident.lexeme}`, + GSErrorName.CompilationError, + ShaderLab._processingPassText, + ident.location + ); + } + this._referencedMRTList[ident.lexeme] = prop; + } + referenceGlobal(ident: string, type: ESymbolType) { if (this._referencedGlobals[ident]) return; @@ -101,7 +121,7 @@ export class VisitorContext { } return; } - const sm = this.passSymbolTable.lookup({ ident, symbolType: type }); + const sm = this._passSymbolTable.lookup(ident, type); if (sm) { this._referencedGlobals[ident] = sm; } diff --git a/packages/shader-lab/src/common/BaseScanner.ts b/packages/shader-lab/src/common/BaseScanner.ts index 40e232c834..685d55b25e 100644 --- a/packages/shader-lab/src/common/BaseScanner.ts +++ b/packages/shader-lab/src/common/BaseScanner.ts @@ -148,7 +148,7 @@ export default class BaseScanner { throwError(pos: ShaderPosition | ShaderRange, ...msgs: any[]) { const error = ShaderLabUtils.createGSError(msgs.join(" "), GSErrorName.ScannerError, this._source, pos); // #if _VERBOSE - Logger.error(error.toString()); + Logger.error(error!.toString()); // #endif throw error; } diff --git a/packages/shader-lab/src/common/BaseSymbolTable.ts b/packages/shader-lab/src/common/BaseSymbolTable.ts index ded560c672..96ae17bfa1 100644 --- a/packages/shader-lab/src/common/BaseSymbolTable.ts +++ b/packages/shader-lab/src/common/BaseSymbolTable.ts @@ -1,5 +1,4 @@ import { Logger } from "@galacean/engine"; -import { GalaceanDataType } from "./types"; export interface IBaseSymbol { readonly ident: string; @@ -11,60 +10,29 @@ export interface IBaseSymbol { export abstract class BaseSymbolTable { protected _table: Map = new Map(); - /** - * Check the equality of two symbol. - */ - abstract symbolEqualCheck(exist: T, newSymbol: T): boolean; - - insert(sm: T) { - const entry = this._table.get(sm.ident) ?? []; - for (let i = 0; i < entry.length; i++) { - if (this.symbolEqualCheck(entry[i], sm)) { - Logger.warn("replace symbol:", sm.ident); - entry[i] = sm; - return; - } - } - entry.push(sm); - this._table.set(sm.ident, entry); - } - - lookup(sm: T & { signature?: GalaceanDataType[] }): R { - const entry = this._table.get(sm.ident) ?? []; - for (const item of entry) { - if (this.symbolEqualCheck(item, sm)) return item as unknown as R; - } - } + abstract insert(sm: T): void; } export class SymbolTableStack> { - private _stack: T[] = []; + stack: T[] = []; get _scope() { - return this._stack[this._stack.length - 1]; + return this.stack[this.stack.length - 1]; } newScope(scope: T) { - this._stack.push(scope); + this.stack.push(scope); } clear() { - this._stack.length = 0; + this.stack.length = 0; } dropScope() { - this._stack.pop(); + this.stack.pop(); } insert(sm: S) { this._scope.insert(sm); } - - lookup(sm: S & { signature?: GalaceanDataType[] }) { - for (let i = this._stack.length - 1; i >= 0; i--) { - const scope = this._stack[i]; - const ret = scope.lookup(sm); - if (ret) return ret; - } - } } diff --git a/packages/shader-lab/src/common/Keywords.ts b/packages/shader-lab/src/common/Keywords.ts index 0ab7b47fa5..c584d29ea9 100644 --- a/packages/shader-lab/src/common/Keywords.ts +++ b/packages/shader-lab/src/common/Keywords.ts @@ -27,6 +27,7 @@ export enum EKeyword { VEC2, VEC3, VEC4, + VEC4_ARRAY, MAT2, MAT3, MAT4, @@ -56,6 +57,8 @@ export enum EKeyword { U_SAMPLER_CUBE, U_SAMPLER2D_ARRAY, STRUCT, + LAYOUT, + LOCATION, VOID, TRUE, FALSE, diff --git a/packages/shader-lab/src/common/types.ts b/packages/shader-lab/src/common/types.ts index 508fafb630..353cc8d179 100644 --- a/packages/shader-lab/src/common/types.ts +++ b/packages/shader-lab/src/common/types.ts @@ -63,7 +63,9 @@ export const KeywordTable = new Map([ ["flat", EKeyword.FLAT], ["smooth", EKeyword.SMOOTH], ["noperspective", EKeyword.NOPERSPECTIVE], - ["centroid", EKeyword.CENTROID] + ["centroid", EKeyword.CENTROID], + ["layout", EKeyword.LAYOUT], + ["location", EKeyword.LOCATION] ]); export enum ETokenType { @@ -209,6 +211,7 @@ export type GalaceanDataType = | EKeyword.U_SAMPLER3D | EKeyword.U_SAMPLER_CUBE | EKeyword.U_SAMPLER2D_ARRAY + | EKeyword.VEC4_ARRAY | typeof TypeAny | string; diff --git a/packages/shader-lab/src/contentParser/ContentSymbolTable.ts b/packages/shader-lab/src/contentParser/ContentSymbolTable.ts new file mode 100644 index 0000000000..31fa6cb6ac --- /dev/null +++ b/packages/shader-lab/src/contentParser/ContentSymbolTable.ts @@ -0,0 +1,33 @@ +import { Logger } from "@galacean/engine"; +import { TokenType } from "../common"; +import { BaseSymbolTable, IBaseSymbol } from "../common/BaseSymbolTable"; + +export interface ISymbol extends IBaseSymbol { + type: number; + value?: any; +} + +export default class ContentSymbolTable extends BaseSymbolTable { + override insert(sm: ISymbol): void { + const entry = this._table.get(sm.ident) ?? []; + for (let i = 0; i < entry.length; i++) { + if (entry[i].type === sm.type) { + Logger.warn("replace symbol:", sm.ident); + entry[i] = sm; + return; + } + } + entry.push(sm); + this._table.set(sm.ident, entry); + } + + lookup(ident: string, type: TokenType): ISymbol | undefined { + const entry = this._table.get(ident); + if (entry) { + for (let length = entry.length, i = 0; i < length; i++) { + const item = entry[i]; + if (item.type === type) return item; + } + } + } +} diff --git a/packages/shader-lab/src/contentParser/ShaderContentParser.ts b/packages/shader-lab/src/contentParser/ShaderContentParser.ts index 06bfc6cc95..70d41731a2 100644 --- a/packages/shader-lab/src/contentParser/ShaderContentParser.ts +++ b/packages/shader-lab/src/contentParser/ShaderContentParser.ts @@ -1,10 +1,10 @@ import { SymbolTableStack } from "../common/BaseSymbolTable"; import { BaseToken } from "../common/BaseToken"; -import { EKeyword, ETokenType } from "../common"; +import { EKeyword, ETokenType, TokenType } from "../common"; import { ShaderPosition } from "../common"; import { KeywordMap } from "./KeywordMap"; import Scanner from "./Scanner"; -import SymbolTable, { ISymbol } from "./SymbolTable"; +import ContentSymbolTable, { ISymbol } from "./ContentSymbolTable"; import { RenderStateDataKey, Color, @@ -56,19 +56,11 @@ export class ShaderContentParser { static _errors: GSError[] = []; - private static _isRenderStateDeclarator(token: BaseToken) { - return RenderStateType.includes(token.type); - } - - private static _isEngineType(token: BaseToken) { - return EngineType.includes(token.type); - } - - private static _symbolTable: SymbolTableStack = new SymbolTableStack(); + private static _symbolTableStack: SymbolTableStack = new SymbolTableStack(); static reset() { this._errors.length = 0; - this._symbolTable.clear(); + this._symbolTableStack.clear(); this._newScope(); } @@ -113,6 +105,23 @@ export class ShaderContentParser { return ret; } + private static _isRenderStateDeclarator(token: BaseToken) { + return RenderStateType.includes(token.type); + } + + private static _isEngineType(token: BaseToken) { + return EngineType.includes(token.type); + } + + private static _lookupSymbolByType(ident: string, type: TokenType): ISymbol | undefined { + const stack = ShaderContentParser._symbolTableStack.stack; + for (let length = stack.length, i = length - 1; i >= 0; i--) { + const symbolTable = stack[i]; + const ret = symbolTable.lookup(ident, type); + if (ret) return ret; + } + } + private static _parseShaderStatements(ret: IShaderContent, scanner: Scanner) { let braceLevel = 1; let start = scanner.getCurPosition(); @@ -147,7 +156,7 @@ export class ShaderContentParser { braceLevel -= 1; if (braceLevel === 0) { this._addGlobalStatement(ret, scanner, start, word.lexeme.length); - this._symbolTable.dropScope(); + this._symbolTableStack.dropScope(); return; } } @@ -183,7 +192,7 @@ export class ShaderContentParser { } else if (ident.lexeme === "=") { const variable = scanner.scanToken(); scanner.scanText(";"); - const sm = this._symbolTable.lookup({ type: stateToken.type, ident: variable.lexeme }); + const sm = ShaderContentParser._lookupSymbolByType(variable.lexeme, stateToken.type); if (!sm?.value) { const error = ShaderLabUtils.createGSError( `Invalid "${stateToken.lexeme}" variable: ${variable.lexeme}`, @@ -204,7 +213,7 @@ export class ShaderContentParser { const renderState = this._parseRenderStatePropList(stateToken.lexeme, scanner); if (isDeclaration) { - this._symbolTable.insert({ ident: ident.lexeme, type: stateToken.type, value: renderState }); + this._symbolTableStack.insert({ ident: ident.lexeme, type: stateToken.type, value: renderState }); } else { Object.assign(ret.renderStates.constantMap, renderState.constantMap); Object.assign(ret.renderStates.variableMap, renderState.variableMap); @@ -214,16 +223,16 @@ export class ShaderContentParser { private static _parseVariableDeclaration(type: number, scanner: Scanner) { const token = scanner.scanToken(); scanner.scanText(";"); - this._symbolTable.insert({ type: token.type, ident: token.lexeme }); + this._symbolTableStack.insert({ type: token.type, ident: token.lexeme }); } private static _newScope() { - const symbolTable = new SymbolTable(); - this._symbolTable.newScope(symbolTable); + const symbolTable = new ContentSymbolTable(); + this._symbolTableStack.newScope(symbolTable); } private static _dropScope() { - this._symbolTable.dropScope(); + this._symbolTableStack.dropScope(); } private static _parseRenderStatePropList(state: string, scanner: Scanner): IRenderStates { diff --git a/packages/shader-lab/src/contentParser/SymbolTable.ts b/packages/shader-lab/src/contentParser/SymbolTable.ts deleted file mode 100644 index 4445123d37..0000000000 --- a/packages/shader-lab/src/contentParser/SymbolTable.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { BaseSymbolTable, IBaseSymbol } from "../common/BaseSymbolTable"; - -export interface ISymbol extends IBaseSymbol { - type: number; - value?: any; -} - -export default class SymbolTable extends BaseSymbolTable { - override symbolEqualCheck(s1: ISymbol, s2: ISymbol): boolean { - return s1.type === s2.type; - } -} diff --git a/packages/shader-lab/src/lalr/CFG.ts b/packages/shader-lab/src/lalr/CFG.ts index 01ff210182..577ccd946a 100644 --- a/packages/shader-lab/src/lalr/CFG.ts +++ b/packages/shader-lab/src/lalr/CFG.ts @@ -1,7 +1,7 @@ // Context Free Grammar of Galacean ShaderLab import { Grammar } from "../parser/Grammar"; -import { ENonTerminal, GrammarSymbol } from "../parser/GrammarSymbol"; +import { NoneTerminal, GrammarSymbol } from "../parser/GrammarSymbol"; import GrammarUtils from "./Utils"; import { EKeyword, ETokenType } from "../common"; import SematicAnalyzer, { TranslationRule } from "../parser/SemanticAnalyzer"; @@ -9,30 +9,30 @@ import { ASTNode } from "../parser/AST"; const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ...GrammarUtils.createProductionWithOptions( - ENonTerminal.gs_shader_program, - [[ENonTerminal.global_declaration], [ENonTerminal.gs_shader_program, ENonTerminal.global_declaration]], + NoneTerminal.gs_shader_program, + [[NoneTerminal.global_declaration], [NoneTerminal.gs_shader_program, NoneTerminal.global_declaration]], ASTNode.GLShaderProgram.pool ), - ...GrammarUtils.createProductionWithOptions(ENonTerminal.global_declaration, [ - [ENonTerminal.precision_specifier], - [ENonTerminal.variable_declaration], - [ENonTerminal.struct_specifier], - [ENonTerminal.function_definition] + ...GrammarUtils.createProductionWithOptions(NoneTerminal.global_declaration, [ + [NoneTerminal.precision_specifier], + [NoneTerminal.variable_declaration], + [NoneTerminal.struct_specifier], + [NoneTerminal.function_definition] ]), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.variable_declaration, + NoneTerminal.variable_declaration, [ [EKeyword.GS_RenderQueueType, ETokenType.ID, ETokenType.SEMICOLON], - [ENonTerminal.fully_specified_type, ETokenType.ID, ETokenType.SEMICOLON], - [ENonTerminal.fully_specified_type, ETokenType.ID, ENonTerminal.array_specifier, ETokenType.SEMICOLON] + [NoneTerminal.fully_specified_type, ETokenType.ID, ETokenType.SEMICOLON], + [NoneTerminal.fully_specified_type, ETokenType.ID, NoneTerminal.array_specifier, ETokenType.SEMICOLON] ], ASTNode.VariableDeclaration.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.ext_builtin_type_specifier_nonarray, + NoneTerminal.ext_builtin_type_specifier_nonarray, [ [EKeyword.VOID], [EKeyword.FLOAT], @@ -80,46 +80,46 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.type_specifier_nonarray, - [[ETokenType.ID], [ENonTerminal.ext_builtin_type_specifier_nonarray]], + NoneTerminal.type_specifier_nonarray, + [[ETokenType.ID], [NoneTerminal.ext_builtin_type_specifier_nonarray]], ASTNode.TypeSpecifierNonArray.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.fully_specified_type, - [[ENonTerminal.type_specifier], [ENonTerminal.type_qualifier, ENonTerminal.type_specifier]], + NoneTerminal.fully_specified_type, + [[NoneTerminal.type_specifier], [NoneTerminal.type_qualifier, NoneTerminal.type_specifier]], ASTNode.FullySpecifiedType.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.type_specifier, + NoneTerminal.type_specifier, [ - [ENonTerminal.type_specifier_nonarray], - [ENonTerminal.ext_builtin_type_specifier_nonarray, ENonTerminal.array_specifier] + [NoneTerminal.type_specifier_nonarray], + [NoneTerminal.ext_builtin_type_specifier_nonarray, NoneTerminal.array_specifier] ], ASTNode.TypeSpecifier.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.type_qualifier, - [[ENonTerminal.single_type_qualifier], [ENonTerminal.type_qualifier, ENonTerminal.single_type_qualifier]], + NoneTerminal.type_qualifier, + [[NoneTerminal.single_type_qualifier], [NoneTerminal.type_qualifier, NoneTerminal.single_type_qualifier]], ASTNode.TypeQualifier.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.single_type_qualifier, + NoneTerminal.single_type_qualifier, [ - [ENonTerminal.storage_qualifier], - [ENonTerminal.precision_qualifier], - [ENonTerminal.interpolation_qualifier], - [ENonTerminal.invariant_qualifier], + [NoneTerminal.storage_qualifier], + [NoneTerminal.precision_qualifier], + [NoneTerminal.interpolation_qualifier], + [NoneTerminal.invariant_qualifier], [EKeyword.PRECISE] ], ASTNode.SingleTypeQualifier.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.storage_qualifier, + NoneTerminal.storage_qualifier, [[EKeyword.CONST], [EKeyword.IN], [EKeyword.INOUT], [EKeyword.OUT], [EKeyword.CENTROID]], // #if _VERBOSE ASTNode.StorageQualifier.pool @@ -127,7 +127,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.interpolation_qualifier, + NoneTerminal.interpolation_qualifier, [[EKeyword.SMOOTH], [EKeyword.FLAT]], // #if _VERBOSE ASTNode.InterpolationQualifier.pool @@ -135,7 +135,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.invariant_qualifier, + NoneTerminal.invariant_qualifier, [[EKeyword.INVARIANT]], // #if _VERBOSE ASTNode.InvariantQualifier.pool @@ -143,7 +143,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.precision_qualifier, + NoneTerminal.precision_qualifier, [[EKeyword.HIGHP], [EKeyword.MEDIUMP], [EKeyword.LOWP]], // #if _VERBOSE ASTNode.PrecisionQualifier.pool @@ -151,20 +151,20 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.struct_specifier, + NoneTerminal.struct_specifier, [ [ EKeyword.STRUCT, ETokenType.ID, ETokenType.LEFT_BRACE, - ENonTerminal.struct_declaration_list, + NoneTerminal.struct_declaration_list, ETokenType.RIGHT_BRACE, ETokenType.SEMICOLON ], [ EKeyword.STRUCT, ETokenType.LEFT_BRACE, - ENonTerminal.struct_declaration_list, + NoneTerminal.struct_declaration_list, ETokenType.RIGHT_BRACE, ETokenType.SEMICOLON ] @@ -173,84 +173,100 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.struct_declaration_list, - [[ENonTerminal.struct_declaration], [ENonTerminal.struct_declaration_list, ENonTerminal.struct_declaration]], + NoneTerminal.struct_declaration_list, + [[NoneTerminal.struct_declaration], [NoneTerminal.struct_declaration_list, NoneTerminal.struct_declaration]], ASTNode.StructDeclarationList.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.struct_declaration, + NoneTerminal.struct_declaration, [ - [ENonTerminal.type_specifier, ENonTerminal.struct_declarator_list, ETokenType.SEMICOLON], + [NoneTerminal.type_specifier, NoneTerminal.struct_declarator_list, ETokenType.SEMICOLON], [ - ENonTerminal.type_qualifier, - ENonTerminal.type_specifier, - ENonTerminal.struct_declarator_list, + NoneTerminal.type_qualifier, + NoneTerminal.type_specifier, + NoneTerminal.struct_declarator_list, ETokenType.SEMICOLON - ] + ], + [NoneTerminal.layout_qualifier, NoneTerminal.type_specifier, NoneTerminal.struct_declarator, ETokenType.SEMICOLON] ], ASTNode.StructDeclaration.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.struct_declarator_list, + NoneTerminal.layout_qualifier, + [ + [ + EKeyword.LAYOUT, + ETokenType.LEFT_PAREN, + EKeyword.LOCATION, + ETokenType.EQUAL, + ETokenType.INT_CONSTANT, + ETokenType.RIGHT_PAREN + ] + ], + ASTNode.LayoutQualifier.pool + ), + + ...GrammarUtils.createProductionWithOptions( + NoneTerminal.struct_declarator_list, [ - [ENonTerminal.struct_declarator], - [ENonTerminal.struct_declarator_list, ETokenType.COMMA, ENonTerminal.struct_declarator] + [NoneTerminal.struct_declarator], + [NoneTerminal.struct_declarator_list, ETokenType.COMMA, NoneTerminal.struct_declarator] ], ASTNode.StructDeclaratorList.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.struct_declarator, - [[ETokenType.ID], [ETokenType.ID, ENonTerminal.array_specifier]], + NoneTerminal.struct_declarator, + [[ETokenType.ID], [ETokenType.ID, NoneTerminal.array_specifier]], ASTNode.StructDeclarator.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.array_specifier, + NoneTerminal.array_specifier, [ [ETokenType.LEFT_BRACKET, ETokenType.RIGHT_BRACKET], - [ETokenType.LEFT_BRACKET, ENonTerminal.integer_constant_expression, ETokenType.RIGHT_BRACKET] + [ETokenType.LEFT_BRACKET, NoneTerminal.integer_constant_expression, ETokenType.RIGHT_BRACKET] ], ASTNode.ArraySpecifier.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.integer_constant_expression_operator, + NoneTerminal.integer_constant_expression_operator, [[ETokenType.PLUS], [ETokenType.DASH], [ETokenType.STAR], [ETokenType.SLASH], [ETokenType.PERCENT]], ASTNode.IntegerConstantExpressionOperator.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.integer_constant_expression, + NoneTerminal.integer_constant_expression, [ - [ENonTerminal.variable_identifier], + [NoneTerminal.variable_identifier], [ETokenType.INT_CONSTANT], [ - ENonTerminal.integer_constant_expression, - ENonTerminal.integer_constant_expression_operator, + NoneTerminal.integer_constant_expression, + NoneTerminal.integer_constant_expression_operator, ETokenType.INT_CONSTANT ], [ - ENonTerminal.integer_constant_expression, - ENonTerminal.integer_constant_expression_operator, - ENonTerminal.variable_identifier + NoneTerminal.integer_constant_expression, + NoneTerminal.integer_constant_expression_operator, + NoneTerminal.variable_identifier ] ], ASTNode.IntegerConstantExpression.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.conditional_expression, + NoneTerminal.conditional_expression, [ - [ENonTerminal.logical_or_expression], + [NoneTerminal.logical_or_expression], [ - ENonTerminal.logical_or_expression, + NoneTerminal.logical_or_expression, ETokenType.QUESTION, - ENonTerminal.expression, + NoneTerminal.expression, ETokenType.COLON, - ENonTerminal.assignment_expression + NoneTerminal.assignment_expression ] ], // #if _VERBOSE @@ -259,10 +275,10 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.logical_or_expression, + NoneTerminal.logical_or_expression, [ - [ENonTerminal.logical_xor_expression], - [ENonTerminal.logical_or_expression, ETokenType.OR_OP, ENonTerminal.logical_xor_expression] + [NoneTerminal.logical_xor_expression], + [NoneTerminal.logical_or_expression, ETokenType.OR_OP, NoneTerminal.logical_xor_expression] ], // #if _VERBOSE ASTNode.LogicalOrExpression.pool @@ -270,10 +286,10 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.logical_xor_expression, + NoneTerminal.logical_xor_expression, [ - [ENonTerminal.logical_and_expression], - [ENonTerminal.logical_xor_expression, ETokenType.XOR_OP, ENonTerminal.logical_and_expression] + [NoneTerminal.logical_and_expression], + [NoneTerminal.logical_xor_expression, ETokenType.XOR_OP, NoneTerminal.logical_and_expression] ], // #if _VERBOSE ASTNode.LogicalXorExpression.pool @@ -281,10 +297,10 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.logical_and_expression, + NoneTerminal.logical_and_expression, [ - [ENonTerminal.inclusive_or_expression], - [ENonTerminal.logical_and_expression, ETokenType.AND_OP, ENonTerminal.inclusive_or_expression] + [NoneTerminal.inclusive_or_expression], + [NoneTerminal.logical_and_expression, ETokenType.AND_OP, NoneTerminal.inclusive_or_expression] ], // #if _VERBOSE ASTNode.LogicalAndExpression.pool @@ -292,10 +308,10 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.inclusive_or_expression, + NoneTerminal.inclusive_or_expression, [ - [ENonTerminal.exclusive_or_expression], - [ENonTerminal.inclusive_or_expression, ETokenType.VERTICAL_BAR, ENonTerminal.exclusive_or_expression] + [NoneTerminal.exclusive_or_expression], + [NoneTerminal.inclusive_or_expression, ETokenType.VERTICAL_BAR, NoneTerminal.exclusive_or_expression] ], // #if _VERBOSE ASTNode.InclusiveOrExpression.pool @@ -303,10 +319,10 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.exclusive_or_expression, + NoneTerminal.exclusive_or_expression, [ - [ENonTerminal.and_expression], - [ENonTerminal.exclusive_or_expression, ETokenType.CARET, ENonTerminal.and_expression] + [NoneTerminal.and_expression], + [NoneTerminal.exclusive_or_expression, ETokenType.CARET, NoneTerminal.and_expression] ], // #if _VERBOSE ASTNode.ExclusiveOrExpression.pool @@ -314,10 +330,10 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.and_expression, + NoneTerminal.and_expression, [ - [ENonTerminal.equality_expression], - [ENonTerminal.and_expression, ETokenType.AMPERSAND, ENonTerminal.equality_expression] + [NoneTerminal.equality_expression], + [NoneTerminal.and_expression, ETokenType.AMPERSAND, NoneTerminal.equality_expression] ], // #if _VERBOSE ASTNode.AndExpression.pool @@ -325,11 +341,11 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.equality_expression, + NoneTerminal.equality_expression, [ - [ENonTerminal.relational_expression], - [ENonTerminal.equality_expression, ETokenType.EQ_OP, ENonTerminal.relational_expression], - [ENonTerminal.equality_expression, ETokenType.NE_OP, ENonTerminal.relational_expression] + [NoneTerminal.relational_expression], + [NoneTerminal.equality_expression, ETokenType.EQ_OP, NoneTerminal.relational_expression], + [NoneTerminal.equality_expression, ETokenType.NE_OP, NoneTerminal.relational_expression] ], // #if _VERBOSE ASTNode.EqualityExpression.pool @@ -337,13 +353,13 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.relational_expression, + NoneTerminal.relational_expression, [ - [ENonTerminal.shift_expression], - [ENonTerminal.relational_expression, ETokenType.LEFT_ANGLE, ENonTerminal.shift_expression], - [ENonTerminal.relational_expression, ETokenType.RIGHT_ANGLE, ENonTerminal.shift_expression], - [ENonTerminal.relational_expression, ETokenType.LE_OP, ENonTerminal.shift_expression], - [ENonTerminal.relational_expression, ETokenType.GE_OP, ENonTerminal.shift_expression] + [NoneTerminal.shift_expression], + [NoneTerminal.relational_expression, ETokenType.LEFT_ANGLE, NoneTerminal.shift_expression], + [NoneTerminal.relational_expression, ETokenType.RIGHT_ANGLE, NoneTerminal.shift_expression], + [NoneTerminal.relational_expression, ETokenType.LE_OP, NoneTerminal.shift_expression], + [NoneTerminal.relational_expression, ETokenType.GE_OP, NoneTerminal.shift_expression] ], // #if _VERBOSE ASTNode.RelationalExpression.pool @@ -351,11 +367,11 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.shift_expression, + NoneTerminal.shift_expression, [ - [ENonTerminal.additive_expression], - [ENonTerminal.shift_expression, ETokenType.LEFT_OP, ENonTerminal.additive_expression], - [ENonTerminal.shift_expression, ETokenType.RIGHT_OP, ENonTerminal.additive_expression] + [NoneTerminal.additive_expression], + [NoneTerminal.shift_expression, ETokenType.LEFT_OP, NoneTerminal.additive_expression], + [NoneTerminal.shift_expression, ETokenType.RIGHT_OP, NoneTerminal.additive_expression] ], // #if _VERBOSE ASTNode.ShiftExpression.pool @@ -363,11 +379,11 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.additive_expression, + NoneTerminal.additive_expression, [ - [ENonTerminal.multiplicative_expression], - [ENonTerminal.additive_expression, ETokenType.PLUS, ENonTerminal.multiplicative_expression], - [ENonTerminal.additive_expression, ETokenType.DASH, ENonTerminal.multiplicative_expression] + [NoneTerminal.multiplicative_expression], + [NoneTerminal.additive_expression, ETokenType.PLUS, NoneTerminal.multiplicative_expression], + [NoneTerminal.additive_expression, ETokenType.DASH, NoneTerminal.multiplicative_expression] ], // #if _VERBOSE ASTNode.AdditiveExpression.pool @@ -375,12 +391,12 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.multiplicative_expression, + NoneTerminal.multiplicative_expression, [ - [ENonTerminal.unary_expression], - [ENonTerminal.multiplicative_expression, ETokenType.STAR, ENonTerminal.unary_expression], - [ENonTerminal.multiplicative_expression, ETokenType.SLASH, ENonTerminal.unary_expression], - [ENonTerminal.multiplicative_expression, ETokenType.PERCENT, ENonTerminal.unary_expression] + [NoneTerminal.unary_expression], + [NoneTerminal.multiplicative_expression, ETokenType.STAR, NoneTerminal.unary_expression], + [NoneTerminal.multiplicative_expression, ETokenType.SLASH, NoneTerminal.unary_expression], + [NoneTerminal.multiplicative_expression, ETokenType.PERCENT, NoneTerminal.unary_expression] ], // #if _VERBOSE ASTNode.MultiplicativeExpression.pool @@ -388,12 +404,12 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.unary_expression, + NoneTerminal.unary_expression, [ - [ENonTerminal.postfix_expression], - [ETokenType.INC_OP, ENonTerminal.unary_expression], - [ETokenType.DEC_OP, ENonTerminal.unary_expression], - [ENonTerminal.unary_operator, ENonTerminal.unary_expression] + [NoneTerminal.postfix_expression], + [ETokenType.INC_OP, NoneTerminal.unary_expression], + [ETokenType.DEC_OP, NoneTerminal.unary_expression], + [NoneTerminal.unary_operator, NoneTerminal.unary_expression] ], // #if _VERBOSE ASTNode.UnaryExpression.pool @@ -401,7 +417,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.unary_operator, + NoneTerminal.unary_operator, [[ETokenType.PLUS], [ETokenType.DASH], [ETokenType.BANG], [ETokenType.TILDE]], // #if _VERBOSE ASTNode.UnaryOperator.pool @@ -409,52 +425,52 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.postfix_expression, + NoneTerminal.postfix_expression, [ - [ENonTerminal.primary_expression], - [ENonTerminal.postfix_expression, ETokenType.LEFT_BRACKET, ENonTerminal.expression, ETokenType.RIGHT_BRACKET], - [ENonTerminal.function_call], - [ENonTerminal.postfix_expression, ETokenType.DOT, ETokenType.ID], - [ENonTerminal.postfix_expression, ETokenType.DOT, ENonTerminal.function_call], - [ENonTerminal.postfix_expression, ETokenType.INC_OP], - [ENonTerminal.postfix_expression, ETokenType.DEC_OP] + [NoneTerminal.primary_expression], + [NoneTerminal.postfix_expression, ETokenType.LEFT_BRACKET, NoneTerminal.expression, ETokenType.RIGHT_BRACKET], + [NoneTerminal.function_call], + [NoneTerminal.postfix_expression, ETokenType.DOT, ETokenType.ID], + [NoneTerminal.postfix_expression, ETokenType.DOT, NoneTerminal.function_call], + [NoneTerminal.postfix_expression, ETokenType.INC_OP], + [NoneTerminal.postfix_expression, ETokenType.DEC_OP] ], ASTNode.PostfixExpression.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.primary_expression, + NoneTerminal.primary_expression, [ - [ENonTerminal.variable_identifier], + [NoneTerminal.variable_identifier], [ETokenType.INT_CONSTANT], [ETokenType.FLOAT_CONSTANT], [EKeyword.TRUE], [EKeyword.FALSE], - [ETokenType.LEFT_PAREN, ENonTerminal.expression, ETokenType.RIGHT_PAREN] + [ETokenType.LEFT_PAREN, NoneTerminal.expression, ETokenType.RIGHT_PAREN] ], ASTNode.PrimaryExpression.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.expression, + NoneTerminal.expression, [ - [ENonTerminal.assignment_expression], - [ENonTerminal.expression, ETokenType.COMMA, ENonTerminal.assignment_expression] + [NoneTerminal.assignment_expression], + [NoneTerminal.expression, ETokenType.COMMA, NoneTerminal.assignment_expression] ], ASTNode.Expression.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.assignment_expression, + NoneTerminal.assignment_expression, [ - [ENonTerminal.conditional_expression], - [ENonTerminal.unary_expression, ENonTerminal.assignment_operator, ENonTerminal.assignment_expression] + [NoneTerminal.conditional_expression], + [NoneTerminal.unary_expression, NoneTerminal.assignment_operator, NoneTerminal.assignment_expression] ], ASTNode.AssignmentExpression.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.assignment_operator, + NoneTerminal.assignment_operator, [ [ETokenType.EQUAL], [ETokenType.MUL_ASSIGN], @@ -474,117 +490,117 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_call, - [[ENonTerminal.function_call_generic]], + NoneTerminal.function_call, + [[NoneTerminal.function_call_generic]], ASTNode.FunctionCall.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_call_generic, + NoneTerminal.function_call_generic, [ [ - ENonTerminal.function_identifier, + NoneTerminal.function_identifier, ETokenType.LEFT_PAREN, - ENonTerminal.function_call_parameter_list, + NoneTerminal.function_call_parameter_list, ETokenType.RIGHT_PAREN ], - [ENonTerminal.function_identifier, ETokenType.LEFT_PAREN, ETokenType.RIGHT_PAREN], - [ENonTerminal.function_identifier, EKeyword.VOID, ETokenType.RIGHT_PAREN] + [NoneTerminal.function_identifier, ETokenType.LEFT_PAREN, ETokenType.RIGHT_PAREN], + [NoneTerminal.function_identifier, EKeyword.VOID, ETokenType.RIGHT_PAREN] ], ASTNode.FunctionCallGeneric.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_call_parameter_list, + NoneTerminal.function_call_parameter_list, [ - [ENonTerminal.assignment_expression], - [ENonTerminal.function_call_parameter_list, ETokenType.COMMA, ENonTerminal.assignment_expression] + [NoneTerminal.assignment_expression], + [NoneTerminal.function_call_parameter_list, ETokenType.COMMA, NoneTerminal.assignment_expression] ], ASTNode.FunctionCallParameterList.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_identifier, - [[ENonTerminal.type_specifier]], + NoneTerminal.function_identifier, + [[NoneTerminal.type_specifier]], ASTNode.FunctionIdentifier.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_definition, - [[ENonTerminal.function_prototype, ENonTerminal.compound_statement_no_scope]], + NoneTerminal.function_definition, + [[NoneTerminal.function_prototype, NoneTerminal.compound_statement_no_scope]], ASTNode.FunctionDefinition.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_prototype, - [[ENonTerminal.function_declarator, ETokenType.RIGHT_PAREN]], + NoneTerminal.function_prototype, + [[NoneTerminal.function_declarator, ETokenType.RIGHT_PAREN]], ASTNode.FunctionProtoType.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_declarator, - [[ENonTerminal.function_header], [ENonTerminal.function_header, ENonTerminal.function_parameter_list]], + NoneTerminal.function_declarator, + [[NoneTerminal.function_header], [NoneTerminal.function_header, NoneTerminal.function_parameter_list]], ASTNode.FunctionDeclarator.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_header, - [[ENonTerminal.fully_specified_type, ETokenType.ID, ETokenType.LEFT_PAREN]], + NoneTerminal.function_header, + [[NoneTerminal.fully_specified_type, ETokenType.ID, ETokenType.LEFT_PAREN]], ASTNode.FunctionHeader.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.function_parameter_list, + NoneTerminal.function_parameter_list, [ - [ENonTerminal.parameter_declaration], - [ENonTerminal.function_parameter_list, ETokenType.COMMA, ENonTerminal.parameter_declaration] + [NoneTerminal.parameter_declaration], + [NoneTerminal.function_parameter_list, ETokenType.COMMA, NoneTerminal.parameter_declaration] ], ASTNode.FunctionParameterList.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.parameter_declaration, - [[ENonTerminal.type_qualifier, ENonTerminal.parameter_declarator], [ENonTerminal.parameter_declarator]], + NoneTerminal.parameter_declaration, + [[NoneTerminal.type_qualifier, NoneTerminal.parameter_declarator], [NoneTerminal.parameter_declarator]], ASTNode.ParameterDeclaration.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.parameter_declarator, + NoneTerminal.parameter_declarator, [ - [ENonTerminal.type_specifier, ETokenType.ID], - [ENonTerminal.type_specifier, ETokenType.ID, ENonTerminal.array_specifier] + [NoneTerminal.type_specifier, ETokenType.ID], + [NoneTerminal.type_specifier, ETokenType.ID, NoneTerminal.array_specifier] ], ASTNode.ParameterDeclarator.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.statement_list, - [[ENonTerminal.statement], [ENonTerminal.statement_list, ENonTerminal.statement]], + NoneTerminal.statement_list, + [[NoneTerminal.statement], [NoneTerminal.statement_list, NoneTerminal.statement]], ASTNode.StatementList.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.statement, - [[ENonTerminal.compound_statement], [ENonTerminal.simple_statement]], + NoneTerminal.statement, + [[NoneTerminal.compound_statement], [NoneTerminal.simple_statement]], // #if _VERBOSE ASTNode.Statement.pool // #endif ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.compound_statement_no_scope, + NoneTerminal.compound_statement_no_scope, [ [ETokenType.LEFT_BRACE, ETokenType.RIGHT_BRACE], - [ETokenType.LEFT_BRACE, ENonTerminal.statement_list, ETokenType.RIGHT_BRACE] + [ETokenType.LEFT_BRACE, NoneTerminal.statement_list, ETokenType.RIGHT_BRACE] ], ASTNode.CompoundStatementNoScope.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.compound_statement, + NoneTerminal.compound_statement, [ [ETokenType.LEFT_BRACE, ETokenType.RIGHT_BRACE], - [ENonTerminal.scope_brace, ENonTerminal.statement_list, ENonTerminal.scope_end_brace] + [NoneTerminal.scope_brace, NoneTerminal.statement_list, NoneTerminal.scope_end_brace] ], // #if _VERBOSE ASTNode.CompoundStatement.pool @@ -592,13 +608,13 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.simple_statement, + NoneTerminal.simple_statement, [ - [ENonTerminal.declaration], - [ENonTerminal.expression_statement], - [ENonTerminal.selection_statement], - [ENonTerminal.iteration_statement], - [ENonTerminal.jump_statement] + [NoneTerminal.declaration], + [NoneTerminal.expression_statement], + [NoneTerminal.selection_statement], + [NoneTerminal.iteration_statement], + [NoneTerminal.jump_statement] ], // #if _VERBOSE ASTNode.SimpleStatement.pool @@ -606,72 +622,72 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.declaration, + NoneTerminal.declaration, [ - [ENonTerminal.function_prototype, ETokenType.SEMICOLON], - [ENonTerminal.init_declarator_list, ETokenType.SEMICOLON], + [NoneTerminal.function_prototype, ETokenType.SEMICOLON], + [NoneTerminal.init_declarator_list, ETokenType.SEMICOLON], [ EKeyword.PRECISION, - ENonTerminal.precision_qualifier, - ENonTerminal.ext_builtin_type_specifier_nonarray, + NoneTerminal.precision_qualifier, + NoneTerminal.ext_builtin_type_specifier_nonarray, ETokenType.SEMICOLON ], - [ENonTerminal.type_qualifier, ETokenType.ID, ETokenType.SEMICOLON], - [ENonTerminal.type_qualifier, ETokenType.ID, ENonTerminal.identifier_list, ETokenType.SEMICOLON] + [NoneTerminal.type_qualifier, ETokenType.ID, ETokenType.SEMICOLON], + [NoneTerminal.type_qualifier, ETokenType.ID, NoneTerminal.identifier_list, ETokenType.SEMICOLON] ], ASTNode.Declaration.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.identifier_list, + NoneTerminal.identifier_list, [ [ETokenType.COMMA, ETokenType.ID], - [ENonTerminal.identifier_list, ETokenType.COMMA, ETokenType.ID] + [NoneTerminal.identifier_list, ETokenType.COMMA, ETokenType.ID] ], ASTNode.IdentifierList.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.init_declarator_list, + NoneTerminal.init_declarator_list, [ - [ENonTerminal.single_declaration], - [ENonTerminal.init_declarator_list, ETokenType.COMMA, ETokenType.ID], - [ENonTerminal.init_declarator_list, ETokenType.COMMA, ETokenType.ID, ENonTerminal.array_specifier], + [NoneTerminal.single_declaration], + [NoneTerminal.init_declarator_list, ETokenType.COMMA, ETokenType.ID], + [NoneTerminal.init_declarator_list, ETokenType.COMMA, ETokenType.ID, NoneTerminal.array_specifier], [ - ENonTerminal.init_declarator_list, + NoneTerminal.init_declarator_list, ETokenType.COMMA, ETokenType.ID, - ENonTerminal.array_specifier, + NoneTerminal.array_specifier, ETokenType.EQUAL, - ENonTerminal.initializer + NoneTerminal.initializer ], - [ENonTerminal.init_declarator_list, ETokenType.COMMA, ETokenType.ID, ETokenType.EQUAL, ENonTerminal.initializer] + [NoneTerminal.init_declarator_list, ETokenType.COMMA, ETokenType.ID, ETokenType.EQUAL, NoneTerminal.initializer] ], ASTNode.InitDeclaratorList.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.single_declaration, + NoneTerminal.single_declaration, [ - [ENonTerminal.fully_specified_type, ETokenType.ID], - [ENonTerminal.fully_specified_type, ETokenType.ID, ENonTerminal.array_specifier], + [NoneTerminal.fully_specified_type, ETokenType.ID], + [NoneTerminal.fully_specified_type, ETokenType.ID, NoneTerminal.array_specifier], [ - ENonTerminal.fully_specified_type, + NoneTerminal.fully_specified_type, ETokenType.ID, - ENonTerminal.array_specifier, + NoneTerminal.array_specifier, ETokenType.EQUAL, - ENonTerminal.initializer + NoneTerminal.initializer ], - [ENonTerminal.fully_specified_type, ETokenType.ID, ETokenType.EQUAL, ENonTerminal.initializer] + [NoneTerminal.fully_specified_type, ETokenType.ID, ETokenType.EQUAL, NoneTerminal.initializer] ], ASTNode.SingleDeclaration.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.initializer, + NoneTerminal.initializer, [ - [ENonTerminal.assignment_expression], - [ETokenType.LEFT_BRACE, ENonTerminal.initializer_list, ETokenType.RIGHT_BRACE] + [NoneTerminal.assignment_expression], + [ETokenType.LEFT_BRACE, NoneTerminal.initializer_list, ETokenType.RIGHT_BRACE] ], // #if _VERBOSE ASTNode.Initializer.pool @@ -679,16 +695,16 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.initializer_list, - [[ENonTerminal.initializer], [ENonTerminal.initializer_list, ETokenType.COMMA, ENonTerminal.initializer]], + NoneTerminal.initializer_list, + [[NoneTerminal.initializer], [NoneTerminal.initializer_list, ETokenType.COMMA, NoneTerminal.initializer]], // #if _VERBOSE ASTNode.InitializerList.pool // #endif ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.expression_statement, - [[ETokenType.SEMICOLON], [ENonTerminal.expression, ETokenType.SEMICOLON]], + NoneTerminal.expression_statement, + [[ETokenType.SEMICOLON], [NoneTerminal.expression, ETokenType.SEMICOLON]], // #if _VERBOSE ASTNode.ExpressionStatement.pool // #endif @@ -696,17 +712,17 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ // dangling else ambiguity ...GrammarUtils.createProductionWithOptions( - ENonTerminal.selection_statement, + NoneTerminal.selection_statement, [ - [EKeyword.IF, ETokenType.LEFT_PAREN, ENonTerminal.expression, ETokenType.RIGHT_PAREN, ENonTerminal.statement], + [EKeyword.IF, ETokenType.LEFT_PAREN, NoneTerminal.expression, ETokenType.RIGHT_PAREN, NoneTerminal.statement], [ EKeyword.IF, ETokenType.LEFT_PAREN, - ENonTerminal.expression, + NoneTerminal.expression, ETokenType.RIGHT_PAREN, - ENonTerminal.statement, + NoneTerminal.statement, EKeyword.ELSE, - ENonTerminal.statement + NoneTerminal.statement ] ], // #if _VERBOSE @@ -715,16 +731,16 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.iteration_statement, + NoneTerminal.iteration_statement, [ - [EKeyword.WHILE, ETokenType.LEFT_PAREN, ENonTerminal.condition, ETokenType.RIGHT_PAREN, ENonTerminal.statement], + [EKeyword.WHILE, ETokenType.LEFT_PAREN, NoneTerminal.condition, ETokenType.RIGHT_PAREN, NoneTerminal.statement], [ EKeyword.FOR, ETokenType.LEFT_PAREN, - ENonTerminal.for_init_statement, - ENonTerminal.for_rest_statement, + NoneTerminal.for_init_statement, + NoneTerminal.for_rest_statement, ETokenType.RIGHT_PAREN, - ENonTerminal.statement + NoneTerminal.statement ] ], // #if _VERBOSE @@ -733,12 +749,12 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.precision_specifier, + NoneTerminal.precision_specifier, [ [ EKeyword.PRECISION, - ENonTerminal.precision_qualifier, - ENonTerminal.ext_builtin_type_specifier_nonarray, + NoneTerminal.precision_qualifier, + NoneTerminal.ext_builtin_type_specifier_nonarray, ETokenType.SEMICOLON ] ], @@ -746,18 +762,18 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.for_init_statement, - [[ENonTerminal.expression_statement], [ENonTerminal.declaration]], + NoneTerminal.for_init_statement, + [[NoneTerminal.expression_statement], [NoneTerminal.declaration]], // #if _VERBOSE ASTNode.ForInitStatement.pool // #endif ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.condition, + NoneTerminal.condition, [ - [ENonTerminal.expression], - [ENonTerminal.fully_specified_type, ETokenType.ID, ETokenType.EQUAL, ENonTerminal.initializer] + [NoneTerminal.expression], + [NoneTerminal.fully_specified_type, ETokenType.ID, ETokenType.EQUAL, NoneTerminal.initializer] ], // #if _VERBOSE ASTNode.Condition.pool @@ -765,10 +781,10 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.for_rest_statement, + NoneTerminal.for_rest_statement, [ - [ENonTerminal.conditionopt, ETokenType.SEMICOLON], - [ENonTerminal.conditionopt, ETokenType.SEMICOLON, ENonTerminal.expression] + [NoneTerminal.conditionopt, ETokenType.SEMICOLON], + [NoneTerminal.conditionopt, ETokenType.SEMICOLON, NoneTerminal.expression] ], // #if _VERBOSE ASTNode.ForRestStatement.pool @@ -776,39 +792,39 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.conditionopt, - [[ETokenType.EPSILON], [ENonTerminal.condition]], + NoneTerminal.conditionopt, + [[ETokenType.EPSILON], [NoneTerminal.condition]], // #if _VERBOSE ASTNode.ConditionOpt.pool // #endif ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.jump_statement, + NoneTerminal.jump_statement, [ [EKeyword.CONTINUE, ETokenType.SEMICOLON], [EKeyword.BREAK, ETokenType.SEMICOLON], [EKeyword.RETURN, ETokenType.SEMICOLON], - [EKeyword.RETURN, ENonTerminal.expression, ETokenType.SEMICOLON], + [EKeyword.RETURN, NoneTerminal.expression, ETokenType.SEMICOLON], [EKeyword.DISCARD, ETokenType.SEMICOLON] ], ASTNode.JumpStatement.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.scope_brace, + NoneTerminal.scope_brace, [[ETokenType.LEFT_BRACE]], ASTNode.ScopeBrace.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.scope_end_brace, + NoneTerminal.scope_end_brace, [[ETokenType.RIGHT_BRACE]], ASTNode.ScopeEndBrace.pool ), ...GrammarUtils.createProductionWithOptions( - ENonTerminal.variable_identifier, + NoneTerminal.variable_identifier, [[ETokenType.ID]], ASTNode.VariableIdentifier.pool ) @@ -816,7 +832,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [ const createGrammar = () => Grammar.create( - ENonTerminal.gs_shader_program, + NoneTerminal.gs_shader_program, productionAndRules.map((item) => item[0]) ); diff --git a/packages/shader-lab/src/lalr/LALR1.ts b/packages/shader-lab/src/lalr/LALR1.ts index b83eb78f60..31a3bafe12 100644 --- a/packages/shader-lab/src/lalr/LALR1.ts +++ b/packages/shader-lab/src/lalr/LALR1.ts @@ -1,5 +1,5 @@ import { Grammar } from "../parser/Grammar"; -import { ENonTerminal, GrammarSymbol, Terminal } from "../parser/GrammarSymbol"; +import { NoneTerminal, GrammarSymbol, Terminal } from "../parser/GrammarSymbol"; import State from "./State"; import StateItem from "./StateItem"; import GrammarUtils from "./Utils"; @@ -11,15 +11,15 @@ import { ActionInfo, ActionTable, EAction, GotoTable, StateActionTable, StateGot * The [LALR1](https://web.stanford.edu/class/archive/cs/cs143/cs143.1128/handouts/140%20LALR%20Parsing.pdf) Parser generator */ export class LALR1 { - readonly firstSetMap: Map> = new Map(); - readonly followSetMap: Map> = new Map(); + readonly firstSetMap: Map> = new Map(); + readonly followSetMap: Map> = new Map(); readonly actionTable: StateActionTable = new Map(); readonly gotoTable: StateGotoTable = new Map(); private grammar: Grammar; /** For circle detect */ - private _firstSetNTStack: ENonTerminal[] = []; + private _firstSetNTStack: NoneTerminal[] = []; constructor(grammar: Grammar) { this.grammar = grammar; @@ -58,7 +58,7 @@ export class LALR1 { private _extendStateItem(state: State, item: StateItem) { if (GrammarUtils.isTerminal(item.curSymbol)) return; - const productionList = this.grammar.getProductionList(item.curSymbol); + const productionList = this.grammar.getProductionList(item.curSymbol); if (item.nextSymbol) { let newLookaheadSet = new Set(); @@ -72,7 +72,7 @@ export class LALR1 { terminalExist = true; break; } - lastFirstSet = this.firstSetMap.get(nextSymbol)!; + lastFirstSet = this.firstSetMap.get(nextSymbol)!; for (const t of lastFirstSet) { newLookaheadSet.add(t); } @@ -115,7 +115,7 @@ export class LALR1 { for (const stateItem of state.items) { if (stateItem.canReduce()) { let action: ActionInfo; - if (stateItem.production.goal !== ENonTerminal.START) { + if (stateItem.production.goal !== NoneTerminal.START) { action = { action: EAction.Reduce, target: stateItem.production.id @@ -144,7 +144,7 @@ export class LALR1 { target: newState.id }); } else { - stateGotoTable.set(gs, newState.id); + stateGotoTable.set(gs, newState.id); } newStates.add(newState); @@ -181,7 +181,7 @@ export class LALR1 { } } - private _computeFirstSetForNT(NT: ENonTerminal) { + private _computeFirstSetForNT(NT: NoneTerminal) { // circle detect const idx = this._firstSetNTStack.findIndex((item) => item === NT); if (idx !== -1) { @@ -209,12 +209,12 @@ export class LALR1 { break; } - const succeedFirstSet = this._computeFirstSetForNT(gs); + const succeedFirstSet = this._computeFirstSetForNT(gs); for (const item of succeedFirstSet) { if (item !== ETokenType.EPSILON) firstSet.add(item); } - if (!this.grammar.isNullableNT(gs)) break; + if (!this.grammar.isNullableNT(gs)) break; } if (i === production.derivation.length) firstSet.add(ETokenType.EPSILON); } diff --git a/packages/shader-lab/src/lalr/Production.ts b/packages/shader-lab/src/lalr/Production.ts index f6c6d32087..848a7ccb8f 100644 --- a/packages/shader-lab/src/lalr/Production.ts +++ b/packages/shader-lab/src/lalr/Production.ts @@ -1,14 +1,14 @@ -import { ENonTerminal, GrammarSymbol } from "../parser/GrammarSymbol"; +import { NoneTerminal, GrammarSymbol } from "../parser/GrammarSymbol"; export default class Production { private static _id = 0; static pool: Map = new Map(); - readonly goal: ENonTerminal; + readonly goal: NoneTerminal; readonly derivation: GrammarSymbol[]; readonly id: number; - constructor(goal: ENonTerminal, derivation: GrammarSymbol[]) { + constructor(goal: NoneTerminal, derivation: GrammarSymbol[]) { this.goal = goal; this.derivation = derivation; this.id = Production._id++; diff --git a/packages/shader-lab/src/lalr/StateItem.ts b/packages/shader-lab/src/lalr/StateItem.ts index ccf6bbb845..c065e7d1c3 100644 --- a/packages/shader-lab/src/lalr/StateItem.ts +++ b/packages/shader-lab/src/lalr/StateItem.ts @@ -1,5 +1,5 @@ import { ETokenType } from "../common"; -import { ENonTerminal, Terminal } from "../parser/GrammarSymbol"; +import { NoneTerminal, Terminal } from "../parser/GrammarSymbol"; import Production from "./Production"; import GrammarUtils from "./Utils"; @@ -70,7 +70,7 @@ export default class StateItem { const coreItem = this.production.derivation.map((item) => GrammarUtils.toString(item)); coreItem[this.position] = "." + (coreItem[this.position] ?? ""); - return `${ENonTerminal[this.production.goal]} :=> ${coreItem.join("|")} ;${Array.from(this.lookaheadSet) + return `${NoneTerminal[this.production.goal]} :=> ${coreItem.join("|")} ;${Array.from(this.lookaheadSet) .map((item) => GrammarUtils.toString(item)) .join("/")}`; } diff --git a/packages/shader-lab/src/lalr/Utils.ts b/packages/shader-lab/src/lalr/Utils.ts index 8eb9492fef..25066cbe09 100644 --- a/packages/shader-lab/src/lalr/Utils.ts +++ b/packages/shader-lab/src/lalr/Utils.ts @@ -1,7 +1,7 @@ import { EKeyword, ETokenType, ShaderRange } from "../common"; import { ASTNode, TreeNode } from "../parser/AST"; import { TranslationRule } from "../parser/SemanticAnalyzer"; -import { ENonTerminal, GrammarSymbol } from "../parser/GrammarSymbol"; +import { NoneTerminal, GrammarSymbol } from "../parser/GrammarSymbol"; import Production from "./Production"; import { ActionInfo, EAction } from "./types"; import { ShaderLab } from "../ShaderLab"; @@ -10,18 +10,18 @@ import { NodeChild } from "../parser/types"; export default class GrammarUtils { static isTerminal(sm: GrammarSymbol) { - return sm < ENonTerminal.START; + return sm < NoneTerminal.START; } static toString(sm: GrammarSymbol) { if (this.isTerminal(sm)) { return ETokenType[sm] ?? EKeyword[sm]; } - return ENonTerminal[sm]; + return NoneTerminal[sm]; } static createProductionWithOptions( - goal: ENonTerminal, + goal: NoneTerminal, options: GrammarSymbol[][], /** the ast node */ astTypePool?: ClearableObjectPool< @@ -44,17 +44,6 @@ export default class GrammarUtils { return ret; } - static createProductionOptions(common: GrammarSymbol[], position: number, opts: GrammarSymbol[][]) { - const ret: GrammarSymbol[][] = []; - for (const opt of opts) { - const list = common.slice(0, position); - list.push(...opt); - list.push(...common.slice(position)); - ret.push(list); - } - return ret; - } - static addMapSetItem(map: Map>, k: K, v: T) { const set = map.get(k) ?? new Set(); set.add(v); @@ -81,7 +70,7 @@ export default class GrammarUtils { static printProduction(production: Production) { const deriv = production.derivation.map((gs) => GrammarUtils.toString(gs)).join("|"); - return `${ENonTerminal[production.goal]} :=> ${deriv}`; + return `${NoneTerminal[production.goal]} :=> ${deriv}`; } // #endif } diff --git a/packages/shader-lab/src/lalr/types.ts b/packages/shader-lab/src/lalr/types.ts index f980cfd39f..7a5c37513a 100644 --- a/packages/shader-lab/src/lalr/types.ts +++ b/packages/shader-lab/src/lalr/types.ts @@ -1,9 +1,9 @@ -import { ENonTerminal, Terminal } from "../parser/GrammarSymbol"; +import { NoneTerminal, Terminal } from "../parser/GrammarSymbol"; export type StateActionTable = Map; export type ActionTable = Map; export type StateGotoTable = Map; -export type GotoTable = Map; +export type GotoTable = Map; export enum EAction { Shift = 0, diff --git a/packages/shader-lab/src/lexer/Lexer.ts b/packages/shader-lab/src/lexer/Lexer.ts index ef03d6d192..b62889538d 100644 --- a/packages/shader-lab/src/lexer/Lexer.ts +++ b/packages/shader-lab/src/lexer/Lexer.ts @@ -1,4 +1,3 @@ -import { ShaderRange, ShaderPosition } from "../common"; import { ETokenType, KeywordTable } from "../common"; import { EOF, BaseToken } from "../common/BaseToken"; import LexerUtils from "./Utils"; diff --git a/packages/shader-lab/src/parser/AST.ts b/packages/shader-lab/src/parser/AST.ts index f01ffbdce6..ec968e6966 100644 --- a/packages/shader-lab/src/parser/AST.ts +++ b/packages/shader-lab/src/parser/AST.ts @@ -1,21 +1,30 @@ // #if _VERBOSE import { BuiltinFunction, BuiltinVariable, NonGenericGalaceanType } from "./builtin"; // #endif +import { ClearableObjectPool, IPoolElement } from "@galacean/engine"; import { CodeGenVisitor } from "../codeGen"; -import { ENonTerminal } from "./GrammarSymbol"; -import { BaseToken as Token } from "../common/BaseToken"; -import { EKeyword, ETokenType, TokenType, ShaderRange, GalaceanDataType, TypeAny } from "../common"; +import { EKeyword, ETokenType, GalaceanDataType, ShaderRange, TokenType, TypeAny } from "../common"; +import { BaseToken, BaseToken as Token } from "../common/BaseToken"; +import { ParserUtils } from "../ParserUtils"; +import { ShaderLabUtils } from "../ShaderLabUtils"; +import { NoneTerminal } from "./GrammarSymbol"; import SematicAnalyzer from "./SemanticAnalyzer"; import { ShaderData } from "./ShaderInfo"; import { ESymbolType, FnSymbol, StructSymbol, VarSymbol } from "./symbolTable"; -import { ParserUtils } from "../ParserUtils"; import { IParamInfo, NodeChild, StructProp, SymbolType } from "./types"; -import { ClearableObjectPool, IPoolElement } from "@galacean/engine"; -import { ShaderLabUtils } from "../ShaderLabUtils"; + +function ASTNodeDecorator(nonTerminal: NoneTerminal) { + return function (ASTNode: T) { + ASTNode.prototype.nt = nonTerminal; + (ASTNode).pool = ShaderLabUtils.createObjectPool(ASTNode); + }; +} export abstract class TreeNode implements IPoolElement { + static pool: ClearableObjectPool void }>; + /** The non-terminal in grammar. */ - nt: ENonTerminal; + nt: NoneTerminal; private _children: NodeChild[]; private _location: ShaderRange; @@ -27,12 +36,14 @@ export abstract class TreeNode implements IPoolElement { return this._location; } - set(loc: ShaderRange, children: NodeChild[], nt: ENonTerminal) { - this.nt = nt; + set(loc: ShaderRange, children: NodeChild[]): void { this._location = loc; this._children = children; + this.init(); } + init() {} + dispose(): void {} // Visitor pattern interface for code generation @@ -62,52 +73,36 @@ export namespace ASTNode { sa.semanticStack.push(node); } - export class TrivialNode extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(TrivialNode); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal._ignore); - } - } + @ASTNodeDecorator(NoneTerminal._ignore) + export class TrivialNode extends TreeNode {} + @ASTNodeDecorator(NoneTerminal.scope_brace) export class ScopeBrace extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ScopeBrace); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.scope_brace); - } - override semanticAnalyze(sa: SematicAnalyzer): void { sa.newScope(); } } + @ASTNodeDecorator(NoneTerminal.scope_end_brace) export class ScopeEndBrace extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ScopeEndBrace); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.scope_end_brace); - } - override semanticAnalyze(sa: SematicAnalyzer): void { sa.dropScope(); } } + @ASTNodeDecorator(NoneTerminal.jump_statement) export class JumpStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(JumpStatement); + isFragReturnStatement: boolean; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.jump_statement); + override init(): void { + this.isFragReturnStatement = false; } - // #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. + sa.curFunctionInfo.returnStatement = this; } } - // #endif override codeGen(visitor: CodeGenVisitor): string { return visitor.visitJumpStatement(this); @@ -115,61 +110,26 @@ export namespace ASTNode { } // #if _VERBOSE - export class ConditionOpt extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ConditionOpt); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.conditionopt); - } - } - - export class ForRestStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ForRestStatement); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.for_rest_statement); - } - } - - export class Condition extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(Condition); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.condition); - } - } - - export class ForInitStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ForInitStatement); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.for_init_statement); - } - } + @ASTNodeDecorator(NoneTerminal.conditionopt) + export class ConditionOpt extends TreeNode {} - export class IterationStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(IterationStatement); + @ASTNodeDecorator(NoneTerminal.for_rest_statement) + export class ForRestStatement extends TreeNode {} - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.iteration_statement); - } - } + @ASTNodeDecorator(NoneTerminal.condition) + export class Condition extends TreeNode {} - export class SelectionStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(SelectionStatement); + @ASTNodeDecorator(NoneTerminal.for_init_statement) + export class ForInitStatement extends TreeNode {} - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.selection_statement); - } - } + @ASTNodeDecorator(NoneTerminal.iteration_statement) + export class IterationStatement extends TreeNode {} - export class ExpressionStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ExpressionStatement); + @ASTNodeDecorator(NoneTerminal.selection_statement) + export class SelectionStatement extends TreeNode {} - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.expression_statement); - } - } + @ASTNodeDecorator(NoneTerminal.expression_statement) + export class ExpressionStatement extends TreeNode {} // #endif export abstract class ExpressionAstNode extends TreeNode { @@ -181,33 +141,22 @@ export namespace ASTNode { return this._type ?? TypeAny; } - override set(loc: ShaderRange, children: NodeChild[], nt: ENonTerminal) { - super.set(loc, children, nt); + override init(): void { this._type = undefined; } } // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.initializer_list) export class InitializerList extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(InitializerList); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.initializer_list); - } - override semanticAnalyze(sa: SematicAnalyzer): void { const init = this.children[0] as Initializer | InitializerList; this.type = init.type; } } + @ASTNodeDecorator(NoneTerminal.initializer) export class Initializer extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(Initializer); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.initializer); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -218,14 +167,12 @@ export namespace ASTNode { } // #endif + @ASTNodeDecorator(NoneTerminal.single_declaration) export class SingleDeclaration extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(SingleDeclaration); - typeSpecifier: TypeSpecifier; arraySpecifier?: ArraySpecifier; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.single_declaration); + override init(): void { this.typeSpecifier = undefined; this.arraySpecifier = undefined; } @@ -249,7 +196,7 @@ export namespace ASTNode { sm = new VarSymbol(id.lexeme, symbolType, false, initializer); } - sa.symbolTable.insert(sm); + sa.symbolTableStack.insert(sm); } override codeGen(visitor: CodeGenVisitor): string { @@ -257,9 +204,8 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.fully_specified_type) export class FullySpecifiedType extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FullySpecifiedType); - get qualifierList() { if (this.children.length > 1) { return (this.children[0]).qualifierList; @@ -273,21 +219,12 @@ export namespace ASTNode { get type() { return this.typeSpecifier.type; } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.fully_specified_type); - } } + @ASTNodeDecorator(NoneTerminal.type_qualifier) export class TypeQualifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(TypeQualifier); - qualifierList: EKeyword[]; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.type_qualifier); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length > 1) { this.qualifierList = [ @@ -300,16 +237,11 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.single_type_qualifier) export class SingleTypeQualifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(SingleTypeQualifier); - qualifier: EKeyword; lexeme: string; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.single_type_qualifier); - } - override semanticAnalyze(sa: SematicAnalyzer): void { const child = this.children[0]; if (child instanceof Token) { @@ -329,49 +261,24 @@ export namespace ASTNode { get lexeme(): string { return (this.children[0]).lexeme; } - - override set(loc: ShaderRange, children: NodeChild[], nt: ENonTerminal) { - super.set(loc, children, nt); - } } // #if _VERBOSE - export class StorageQualifier extends BasicTypeQualifier { - static pool = ShaderLabUtils.createObjectPool(StorageQualifier); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.storage_qualifier); - } - } - - export class PrecisionQualifier extends BasicTypeQualifier { - static pool = ShaderLabUtils.createObjectPool(PrecisionQualifier); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.precision_qualifier); - } - } - - export class InterpolationQualifier extends BasicTypeQualifier { - static pool = ShaderLabUtils.createObjectPool(InterpolationQualifier); + @ASTNodeDecorator(NoneTerminal.storage_qualifier) + export class StorageQualifier extends BasicTypeQualifier {} - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.interpolation_qualifier); - } - } + @ASTNodeDecorator(NoneTerminal.precision_qualifier) + export class PrecisionQualifier extends BasicTypeQualifier {} - export class InvariantQualifier extends BasicTypeQualifier { - static pool = ShaderLabUtils.createObjectPool(InvariantQualifier); + @ASTNodeDecorator(NoneTerminal.interpolation_qualifier) + export class InterpolationQualifier extends BasicTypeQualifier {} - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.invariant_qualifier); - } - } + @ASTNodeDecorator(NoneTerminal.invariant_qualifier) + export class InvariantQualifier extends BasicTypeQualifier {} // #endif + @ASTNodeDecorator(NoneTerminal.type_specifier) export class TypeSpecifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(TypeSpecifier); - get type(): GalaceanDataType { return (this.children![0] as TypeSpecifierNonArray).type; } @@ -385,37 +292,23 @@ export namespace ASTNode { get isCustom() { return typeof this.type === "string"; } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.type_specifier); - } } + @ASTNodeDecorator(NoneTerminal.array_specifier) export class ArraySpecifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ArraySpecifier); - get size(): number | undefined { const integerConstantExpr = this.children[1] as IntegerConstantExpression; return integerConstantExpr.value; } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.array_specifier); - } } + @ASTNodeDecorator(NoneTerminal.integer_constant_expression_operator) export class IntegerConstantExpressionOperator extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(IntegerConstantExpressionOperator); - compute: (a: number, b: number) => number; get lexeme(): string { return (this.children[0] as Token).lexeme; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.integer_constant_expression_operator); - } - override semanticAnalyze(sa: SematicAnalyzer): void { const operator = this.children[0] as Token; switch (operator.type) { @@ -435,17 +328,16 @@ export namespace ASTNode { this.compute = (a, b) => a % b; break; default: - sa.error(operator.location, `not implemented operator ${operator.lexeme}`); + sa.reportError(operator.location, `not implemented operator ${operator.lexeme}`); } } } + @ASTNodeDecorator(NoneTerminal.integer_constant_expression) export class IntegerConstantExpression extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(IntegerConstantExpression); - value?: number; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.integer_constant_expression); + + override init(): void { this.value = undefined; } @@ -459,10 +351,10 @@ export namespace ASTNode { else { const id = child as VariableIdentifier; if (!id.symbolInfo) { - sa.error(id.location, "Undeclared symbol:", id.lexeme); + sa.reportError(id.location, `Undeclared symbol: ${id.lexeme}`); } if (!ParserUtils.typeCompatible(EKeyword.INT, id.typeInfo)) { - sa.error(id.location, "Invalid integer."); + sa.reportError(id.location, "Invalid integer."); return; } } @@ -471,14 +363,13 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.type_specifier_nonarray) export class TypeSpecifierNonArray extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(TypeSpecifierNonArray); - type: GalaceanDataType; lexeme: string; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.type_specifier_nonarray); - const tt = children[0]; + + override init(): void { + const tt = this.children[0]; if (tt instanceof Token) { this.type = tt.lexeme; this.lexeme = tt.lexeme; @@ -489,23 +380,20 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.ext_builtin_type_specifier_nonarray) export class ExtBuiltinTypeSpecifierNonArray extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ExtBuiltinTypeSpecifierNonArray); - type: TokenType; lexeme: string; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.ext_builtin_type_specifier_nonarray); + override init(): void { const token = this.children[0] as Token; this.type = token.type; this.lexeme = token.lexeme; } } + @ASTNodeDecorator(NoneTerminal.init_declarator_list) export class InitDeclaratorList extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(InitDeclaratorList); - get typeInfo(): SymbolType { if (this.children.length === 1) { const singleDecl = this.children[0] as SingleDeclaration; @@ -520,62 +408,47 @@ export namespace ASTNode { return initDeclList.typeInfo; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.init_declarator_list); - } - override semanticAnalyze(sa: SematicAnalyzer): void { let sm: VarSymbol; if (this.children.length === 3 || this.children.length === 5) { const id = this.children[2] as Token; sm = new VarSymbol(id.lexeme, this.typeInfo, false, this); - sa.symbolTable.insert(sm); + sa.symbolTableStack.insert(sm); } else if (this.children.length === 4 || this.children.length === 6) { const typeInfo = this.typeInfo; const arraySpecifier = this.children[3] as ArraySpecifier; // #if _VERBOSE if (typeInfo.arraySpecifier && arraySpecifier) { - sa.error(arraySpecifier.location, "Array of array is not supported."); + sa.reportError(arraySpecifier.location, "Array of array is not supported."); } // #endif typeInfo.arraySpecifier = arraySpecifier; const id = this.children[2] as Token; sm = new VarSymbol(id.lexeme, typeInfo, false, this); - sa.symbolTable.insert(sm); + sa.symbolTableStack.insert(sm); } } } + @ASTNodeDecorator(NoneTerminal.identifier_list) export class IdentifierList extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(IdentifierList); - get idList(): Token[] { if (this.children.length === 2) { return [this.children[1] as Token]; } return [...(this.children[0]).idList, this.children[2] as Token]; } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.identifier_list); - } } + @ASTNodeDecorator(NoneTerminal.declaration) export class Declaration extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(Declaration); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.declaration); - } - override codeGen(visitor: CodeGenVisitor): string { return visitor.visitDeclaration(this); } } + @ASTNodeDecorator(NoneTerminal.function_prototype) export class FunctionProtoType extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FunctionProtoType); - private get declarator() { return this.children[0] as FunctionDeclarator; } @@ -596,18 +469,13 @@ export namespace ASTNode { return this.declarator.paramSig; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_prototype); - } - override codeGen(visitor: CodeGenVisitor): string { return visitor.visitFunctionProtoType(this); } } + @ASTNodeDecorator(NoneTerminal.function_declarator) export class FunctionDeclarator extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FunctionDeclarator); - private get header() { return this.children[0] as FunctionHeader; } @@ -632,14 +500,14 @@ export namespace ASTNode { return this.parameterList?.paramSig; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_declarator); + override semanticAnalyze(sa: SematicAnalyzer): void { + sa.curFunctionInfo.returnStatement = null; + sa.curFunctionInfo.header = this; } } + @ASTNodeDecorator(NoneTerminal.function_header) export class FunctionHeader extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FunctionHeader); - get ident() { return this.children[1] as Token; } @@ -647,10 +515,6 @@ export namespace ASTNode { return this.children[0] as FullySpecifiedType; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_header); - } - override semanticAnalyze(sa: SematicAnalyzer): void { sa.newScope(); } @@ -660,9 +524,8 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.function_parameter_list) export class FunctionParameterList extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FunctionParameterList); - get parameterInfoList(): IParamInfo[] { if (this.children.length === 1) { const decl = this.children[0] as ParameterDeclaration; @@ -684,18 +547,13 @@ export namespace ASTNode { } } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_parameter_list); - } - override codeGen(visitor: CodeGenVisitor): string { return visitor.visitFunctionParameterList(this); } } + @ASTNodeDecorator(NoneTerminal.parameter_declaration) export class ParameterDeclaration extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ParameterDeclaration); - get typeQualifier() { if (this.children.length === 2) return this.children[0] as TypeQualifier; } @@ -713,10 +571,6 @@ export namespace ASTNode { return this.parameterDeclarator.ident; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.parameter_declaration); - } - override semanticAnalyze(sa: SematicAnalyzer): void { let declarator: ParameterDeclarator; if (this.children.length === 1) { @@ -725,13 +579,12 @@ export namespace ASTNode { declarator = this.children[1] as ParameterDeclarator; } const varSymbol = new VarSymbol(declarator.ident.lexeme, declarator.typeInfo, false, this); - sa.symbolTable.insert(varSymbol); + sa.symbolTableStack.insert(varSymbol); } } + @ASTNodeDecorator(NoneTerminal.parameter_declarator) export class ParameterDeclarator extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(ParameterDeclarator); - get ident() { return this.children[1] as Token; } @@ -741,62 +594,38 @@ export namespace ASTNode { const arraySpecifier = this.children[2] as ArraySpecifier; return new SymbolType(typeSpecifier.type, typeSpecifier.lexeme, arraySpecifier); } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.parameter_declarator); - } } // #if _VERBOSE - export class SimpleStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(SimpleStatement); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.simple_statement); - } - } + @ASTNodeDecorator(NoneTerminal.simple_statement) + export class SimpleStatement extends TreeNode {} - export class CompoundStatement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(CompoundStatement); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.compound_statement); - } - } + @ASTNodeDecorator(NoneTerminal.compound_statement) + export class CompoundStatement extends TreeNode {} // #endif - export class CompoundStatementNoScope extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(CompoundStatementNoScope); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.compound_statement_no_scope); - } - } + @ASTNodeDecorator(NoneTerminal.compound_statement_no_scope) + export class CompoundStatementNoScope extends TreeNode {} // #if _VERBOSE - export class Statement extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(Statement); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.statement); - } - } + @ASTNodeDecorator(NoneTerminal.statement) + export class Statement extends TreeNode {} // #endif + @ASTNodeDecorator(NoneTerminal.statement_list) export class StatementList extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(StatementList); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.statement_list); - } - override codeGen(visitor: CodeGenVisitor): string { return visitor.visitStatementList(this); } } + @ASTNodeDecorator(NoneTerminal.function_definition) export class FunctionDefinition extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FunctionDefinition); + private _returnStatement?: ASTNode.JumpStatement; + + get returnStatement(): ASTNode.JumpStatement | undefined { + return this._returnStatement; + } get protoType() { return this.children[0] as FunctionProtoType; @@ -806,14 +635,30 @@ export namespace ASTNode { return this.children[1] as CompoundStatementNoScope; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_definition); + override init(): void { + this._returnStatement = undefined; } override semanticAnalyze(sa: SematicAnalyzer): void { sa.dropScope(); const sm = new FnSymbol(this.protoType.ident.lexeme, this); - sa.symbolTable.insert(sm); + sa.symbolTableStack.insert(sm); + + const { curFunctionInfo } = sa; + const { header, returnStatement } = curFunctionInfo; + if (header.returnType.type === EKeyword.VOID) { + if (returnStatement) { + sa.reportError(header.returnType.location, "Return in void function."); + } + } else { + if (!returnStatement) { + sa.reportError(header.returnType.location, `No return statement found.`); + } else { + this._returnStatement = returnStatement; + } + } + curFunctionInfo.header = undefined; + curFunctionInfo.returnStatement = undefined; } override codeGen(visitor: CodeGenVisitor): string { @@ -821,13 +666,8 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.function_call) export class FunctionCall extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(FunctionCall); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_call); - } - override semanticAnalyze(sa: SematicAnalyzer): void { this.type = (this.children[0] as FunctionCallGeneric).type; } @@ -837,13 +677,12 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.function_call_generic) export class FunctionCallGeneric extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(FunctionCallGeneric); - fnSymbol: FnSymbol | StructSymbol | undefined; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_call_generic); + override init(): void { + super.init(); this.fnSymbol = undefined; } @@ -869,10 +708,10 @@ export namespace ASTNode { } // #endif - const fnSymbol = sa.symbolTable.lookup({ ident: fnIdent, symbolType: ESymbolType.FN, signature: paramSig }); + const fnSymbol = sa.lookupSymbolBy(fnIdent, ESymbolType.FN, paramSig); if (!fnSymbol) { // #if _VERBOSE - sa.error(this.location, "No overload function type found: ", functionIdentifier.ident); + sa.reportError(this.location, `No overload function type found: ${functionIdentifier.ident}`); // #endif return; } @@ -882,9 +721,8 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.function_call_parameter_list) export class FunctionCallParameterList extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FunctionCallParameterList); - get paramSig(): GalaceanDataType[] | undefined { if (this.children.length === 1) { const expr = this.children[0] as AssignmentExpression; @@ -911,27 +749,17 @@ export namespace ASTNode { return list.paramNodes.concat([decl]); } } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_call_parameter_list); - } } + @ASTNodeDecorator(NoneTerminal.precision_specifier) export class PrecisionSpecifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(PrecisionSpecifier); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.precision_specifier); - } - override semanticAnalyze(sa: SematicAnalyzer): void { sa.shaderData.globalPrecisions.push(this); } } + @ASTNodeDecorator(NoneTerminal.function_identifier) export class FunctionIdentifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(FunctionIdentifier); - get ident() { const ty = this.children[0] as TypeSpecifier; return ty.type; @@ -946,10 +774,6 @@ export namespace ASTNode { return typeof this.ident !== "string"; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.function_identifier); - } - override semanticAnalyze(sa: SematicAnalyzer): void {} override codeGen(visitor: CodeGenVisitor): string { @@ -957,13 +781,8 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.assignment_expression) export class AssignmentExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(AssignmentExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.assignment_expression); - } - // #if _VERBOSE override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { @@ -978,22 +797,12 @@ export namespace ASTNode { } // #if _VERBOSE - export class AssignmentOperator extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(AssignmentOperator); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.assignment_operator); - } - } + @ASTNodeDecorator(NoneTerminal.assignment_operator) + export class AssignmentOperator extends TreeNode {} // #endif + @ASTNodeDecorator(NoneTerminal.expression) export class Expression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(Expression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.expression); - } - // #if _VERBOSE override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { @@ -1007,13 +816,8 @@ export namespace ASTNode { // #endif } + @ASTNodeDecorator(NoneTerminal.primary_expression) export class PrimaryExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(PrimaryExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.primary_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { const id = this.children[0]; @@ -1040,11 +844,10 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.postfix_expression) export class PostfixExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(PostfixExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.postfix_expression); + override init(): void { + super.init(); if (this.children.length === 1) { const child = this.children[0] as PrimaryExpression | FunctionCall; this.type = child.type; @@ -1057,32 +860,20 @@ export namespace ASTNode { } // #if _VERBOSE - export class UnaryOperator extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(UnaryOperator); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.unary_operator); - } - } - // #endif + @ASTNodeDecorator(NoneTerminal.unary_operator) + export class UnaryOperator extends TreeNode {} - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.unary_expression) export class UnaryExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(UnaryExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.unary_expression); + override init(): void { this.type = (this.children[0] as PostfixExpression).type; } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.multiplicative_expression) export class MultiplicativeExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(MultiplicativeExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.multiplicative_expression); + override init(): void { + super.init(); if (this.children.length === 1) { this.type = (this.children[0] as UnaryExpression).type; } else { @@ -1094,14 +885,11 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.additive_expression) export class AdditiveExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(AdditiveExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.additive_expression); + override init(): void { + super.init(); if (this.children.length === 1) { this.type = (this.children[0] as MultiplicativeExpression).type; } else { @@ -1113,31 +901,17 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.shift_expression) export class ShiftExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(ShiftExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.shift_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { const expr = this.children[0] as ExpressionAstNode; this.type = expr.type; } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.relational_expression) export class RelationalExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(RelationalExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.relational_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1146,16 +920,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.equality_expression) export class EqualityExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(EqualityExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.equality_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1164,16 +931,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.and_expression) export class AndExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(AndExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.and_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1182,16 +942,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.exclusive_or_expression) export class ExclusiveOrExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(ExclusiveOrExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.exclusive_or_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1200,16 +953,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.inclusive_or_expression) export class InclusiveOrExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(InclusiveOrExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.inclusive_or_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1218,16 +964,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.logical_and_expression) export class LogicalAndExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(LogicalAndExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.logical_and_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1236,16 +975,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.logical_xor_expression) export class LogicalXorExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(LogicalXorExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.logical_xor_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1254,16 +986,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.logical_or_expression) export class LogicalOrExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(LogicalOrExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.logical_or_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1272,16 +997,9 @@ export namespace ASTNode { } } } - // #endif - // #if _VERBOSE + @ASTNodeDecorator(NoneTerminal.conditional_expression) export class ConditionalExpression extends ExpressionAstNode { - static pool = ShaderLabUtils.createObjectPool(ConditionalExpression); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.conditional_expression); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 1) { this.type = (this.children[0]).type; @@ -1290,9 +1008,8 @@ export namespace ASTNode { } // #endif + @ASTNodeDecorator(NoneTerminal.struct_specifier) export class StructSpecifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(StructSpecifier); - ident?: Token; get propList(): StructProp[] { @@ -1300,21 +1017,16 @@ export namespace ASTNode { return declList.propList; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.struct_specifier); - } - override semanticAnalyze(sa: SematicAnalyzer): void { if (this.children.length === 6) { this.ident = this.children[1] as Token; - sa.symbolTable.insert(new StructSymbol(this.ident.lexeme, this)); + sa.symbolTableStack.insert(new StructSymbol(this.ident.lexeme, this)); } } } + @ASTNodeDecorator(NoneTerminal.struct_declaration_list) export class StructDeclarationList extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(StructDeclarationList); - get propList(): StructProp[] { if (this.children.length === 1) { return (this.children[0]).propList; @@ -1323,15 +1035,10 @@ export namespace ASTNode { const decl = this.children[1] as StructDeclaration; return [list.propList, decl.propList].flat(); } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.struct_declaration_list); - } } + @ASTNodeDecorator(NoneTerminal.struct_declaration) export class StructDeclaration extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(StructDeclaration); - get typeSpecifier() { if (this.children.length === 3) { return this.children[0] as TypeSpecifier; @@ -1348,23 +1055,38 @@ export namespace ASTNode { get propList(): StructProp[] { const ret: StructProp[] = []; - for (let i = 0; i < this.declaratorList.declaratorList.length; i++) { - const declarator = this.declaratorList.declaratorList[i]; - const typeInfo = new SymbolType(this.typeSpecifier.type, this.typeSpecifier.lexeme, declarator.arraySpecifier); - const prop = new StructProp(typeInfo, declarator.ident); + const firstChild = this.children[0]; + if (firstChild instanceof LayoutQualifier) { + const typeSpecifier = this.children[1] as TypeSpecifier; + const declarator = this.children[2] as StructDeclarator; + const typeInfo = new SymbolType(typeSpecifier.type, typeSpecifier.lexeme); + const prop = new StructProp(typeInfo, declarator.ident, firstChild.index); ret.push(prop); + } else { + for (let i = 0; i < this.declaratorList.declaratorList.length; i++) { + const declarator = this.declaratorList.declaratorList[i]; + const typeInfo = new SymbolType( + this.typeSpecifier.type, + this.typeSpecifier.lexeme, + declarator.arraySpecifier + ); + const prop = new StructProp(typeInfo, declarator.ident); + ret.push(prop); + } } return ret; } + } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.struct_declaration); + @ASTNodeDecorator(NoneTerminal.layout_qualifier) + export class LayoutQualifier extends TreeNode { + get index(): number { + return Number((this.children[4]).lexeme); } } + @ASTNodeDecorator(NoneTerminal.struct_declarator_list) export class StructDeclaratorList extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(StructDeclaratorList); - get declaratorList(): StructDeclarator[] { if (this.children.length === 1) { return [this.children[0] as StructDeclarator]; @@ -1373,15 +1095,10 @@ export namespace ASTNode { return [...list.declaratorList, this.children[1]]; } } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.struct_declarator_list); - } } + @ASTNodeDecorator(NoneTerminal.struct_declarator) export class StructDeclarator extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(StructDeclarator); - get ident() { return this.children[0] as Token; } @@ -1389,26 +1106,17 @@ export namespace ASTNode { get arraySpecifier(): ArraySpecifier | undefined { return this.children[1] as ArraySpecifier; } - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.struct_declarator); - } } + @ASTNodeDecorator(NoneTerminal.variable_declaration) export class VariableDeclaration extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(VariableDeclaration); - - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.variable_declaration); - } - override semanticAnalyze(sa: SematicAnalyzer): void { const type = this.children[0] as FullySpecifiedType; const ident = this.children[1] as Token; let sm: VarSymbol; sm = new VarSymbol(ident.lexeme, new SymbolType(type.type, type.typeSpecifier.lexeme), true, this); - sa.symbolTable.insert(sm); + sa.symbolTableStack.insert(sm); } override codeGen(visitor: CodeGenVisitor): string { @@ -1416,9 +1124,8 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.variable_identifier) export class VariableIdentifier extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(VariableIdentifier); - symbolInfo: | VarSymbol // #if _VERBOSE @@ -1435,10 +1142,6 @@ export namespace ASTNode { return this.symbolInfo?.type; } - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.variable_identifier); - } - override semanticAnalyze(sa: SematicAnalyzer): void { const token = this.children[0] as Token; @@ -1450,10 +1153,10 @@ export namespace ASTNode { } // #endif - this.symbolInfo = sa.symbolTable.lookup({ ident: token.lexeme, symbolType: ESymbolType.VAR }) as VarSymbol; + this.symbolInfo = sa.lookupSymbolBy(token.lexeme, ESymbolType.VAR) as VarSymbol; // #if _VERBOSE if (!this.symbolInfo) { - sa.error(this.location, "undeclared identifier:", token.lexeme); + sa.reportError(this.location, `undeclared identifier: ${token.lexeme}`); } // #endif } @@ -1463,18 +1166,13 @@ export namespace ASTNode { } } + @ASTNodeDecorator(NoneTerminal.gs_shader_program) export class GLShaderProgram extends TreeNode { - static pool = ShaderLabUtils.createObjectPool(GLShaderProgram); - shaderData: ShaderData; - override set(loc: ShaderRange, children: NodeChild[]) { - super.set(loc, children, ENonTerminal.gs_shader_program); - } - override semanticAnalyze(sa: SematicAnalyzer): void { this.shaderData = sa.shaderData; - this.shaderData.symbolTable = sa.symbolTable._scope; + this.shaderData.symbolTable = sa.symbolTableStack._scope; } } } diff --git a/packages/shader-lab/src/parser/Grammar.ts b/packages/shader-lab/src/parser/Grammar.ts index 5222c03bc5..bd53837970 100644 --- a/packages/shader-lab/src/parser/Grammar.ts +++ b/packages/shader-lab/src/parser/Grammar.ts @@ -1,30 +1,30 @@ import { ETokenType } from "../common"; -import { ENonTerminal, GrammarSymbol } from "./GrammarSymbol"; +import { NoneTerminal, GrammarSymbol } from "./GrammarSymbol"; import Production from "../lalr/Production"; export class Grammar { readonly productions: Production[]; - readonly startSymbol: ENonTerminal; + readonly startSymbol: NoneTerminal; - static create(start: ENonTerminal, productions: GrammarSymbol[][]) { + static create(start: NoneTerminal, productions: GrammarSymbol[][]) { const _ps = productions.map((gsl) => { - return new Production(gsl[0], gsl.slice(1)); + return new Production(gsl[0], gsl.slice(1)); }); return new Grammar(start, _ps); } - constructor(start: ENonTerminal, productions: Production[]) { + constructor(start: NoneTerminal, productions: Production[]) { this.startSymbol = start; - productions.unshift(new Production(ENonTerminal.START, [start])); + productions.unshift(new Production(NoneTerminal.START, [start])); this.productions = productions; } - getProductionList(nonTerminal: ENonTerminal) { + getProductionList(nonTerminal: NoneTerminal) { return this.productions.filter((item) => item.goal === nonTerminal); } - isNullableNT(NT: ENonTerminal) { + isNullableNT(NT: NoneTerminal) { return this.productions.find((item) => item.goal === NT && item.derivation[0] === ETokenType.EPSILON); } diff --git a/packages/shader-lab/src/parser/GrammarSymbol.ts b/packages/shader-lab/src/parser/GrammarSymbol.ts index 0dd3789a18..208d00a171 100644 --- a/packages/shader-lab/src/parser/GrammarSymbol.ts +++ b/packages/shader-lab/src/parser/GrammarSymbol.ts @@ -2,7 +2,7 @@ import { TokenType } from "../common"; export type Terminal = TokenType; -export enum ENonTerminal { +export enum NoneTerminal { START = 2000, // galacean gs_shader_program, @@ -72,6 +72,7 @@ export enum ENonTerminal { struct_specifier, struct_declaration_list, struct_declaration, + layout_qualifier, struct_declarator_list, struct_declarator, identifier_list, @@ -107,6 +108,6 @@ export enum ENonTerminal { _ignore } -export type GrammarSymbol = Terminal | ENonTerminal; +export type GrammarSymbol = Terminal | NoneTerminal; export type Derivation = GrammarSymbol[]; diff --git a/packages/shader-lab/src/parser/SemanticAnalyzer.ts b/packages/shader-lab/src/parser/SemanticAnalyzer.ts index b078287d97..0028c9e895 100644 --- a/packages/shader-lab/src/parser/SemanticAnalyzer.ts +++ b/packages/shader-lab/src/parser/SemanticAnalyzer.ts @@ -1,18 +1,22 @@ import { ShaderRange } from "../common"; -import { TreeNode } from "./AST"; +import { ASTNode, TreeNode } from "./AST"; import { GSErrorName } from "../GSError"; import { ShaderData } from "./ShaderInfo"; -import { SymbolInfo, SymbolTable } from "../parser/symbolTable"; +import { ESymbolType, SymbolInfo, TargetSymbolTable } from "../parser/symbolTable"; import { NodeChild } from "./types"; import { SymbolTableStack } from "../common/BaseSymbolTable"; import { ShaderLab } from "../ShaderLab"; +import { NonGenericGalaceanType } from "./builtin"; // #if _VERBOSE import { GSError } from "../GSError"; +// #else +import { Logger } from "@galacean/engine"; // #endif export type TranslationRule = (sa: SematicAnalyzer, ...tokens: NodeChild[]) => T; /** + * @internal * The semantic analyzer of `ShaderLab` compiler. * - Build symbol table * - Static analysis @@ -20,19 +24,22 @@ export type TranslationRule = (sa: SematicAnalyzer, ...tokens: NodeChil export default class SematicAnalyzer { semanticStack: TreeNode[] = []; acceptRule?: TranslationRule = undefined; - symbolTable: SymbolTableStack = new SymbolTableStack(); + symbolTableStack: SymbolTableStack = new SymbolTableStack(); + curFunctionInfo: { + header?: ASTNode.FunctionDeclarator; + returnStatement?: ASTNode.JumpStatement; + } = {}; private _shaderData = new ShaderData(); + private _translationRuleTable: Map = new Map(); // #if _VERBOSE - readonly errors: GSError[] = []; + readonly errors: Error[] = []; // #endif get shaderData() { return this._shaderData; } - private _translationRuleTable: Map = new Map(); - constructor() { this.newScope(); } @@ -40,7 +47,7 @@ export default class SematicAnalyzer { reset() { this.semanticStack.length = 0; this._shaderData = new ShaderData(); - this.symbolTable.clear(); + this.symbolTableStack.clear(); this.newScope(); // #if _VERBOSE this.errors.length = 0; @@ -48,12 +55,12 @@ export default class SematicAnalyzer { } newScope() { - const scope = new SymbolTable(); - this.symbolTable.newScope(scope); + const scope = new TargetSymbolTable(); + this.symbolTableStack.newScope(scope); } dropScope() { - return this.symbolTable.dropScope(); + return this.symbolTableStack.dropScope(); } addTranslationRule(pid: number, rule: TranslationRule) { @@ -64,13 +71,25 @@ export default class SematicAnalyzer { return this._translationRuleTable.get(pid); } - error(loc: ShaderRange, ...param: any[]) { + lookupSymbolBy( + ident: string, + symbolType: ESymbolType, + signature?: NonGenericGalaceanType[], + astNode?: ASTNode.FunctionDefinition + ): SymbolInfo | undefined { + const stack = this.symbolTableStack.stack; + for (let length = stack.length, i = length - 1; i >= 0; i--) { + const symbolTable = stack[i]; + const ret = symbolTable.lookup(ident, symbolType, signature, astNode); + if (ret) return ret; + } + } + + reportError(loc: ShaderRange, message: string): void { // #if _VERBOSE - const err = new GSError(GSErrorName.CompilationError, param.join(""), loc, ShaderLab._processingPassText); - this.errors.push(err); - return err; + this.errors.push(new GSError(GSErrorName.CompilationError, message, loc, ShaderLab._processingPassText)); // #else - throw new Error(param.join("")); + Logger.error(message); // #endif } } diff --git a/packages/shader-lab/src/parser/ShaderInfo.ts b/packages/shader-lab/src/parser/ShaderInfo.ts index dacd0814b9..2954f6671c 100644 --- a/packages/shader-lab/src/parser/ShaderInfo.ts +++ b/packages/shader-lab/src/parser/ShaderInfo.ts @@ -1,8 +1,8 @@ import { ASTNode } from "./AST"; -import { SymbolTable } from "../parser/symbolTable"; +import { TargetSymbolTable } from "../parser/symbolTable"; export class ShaderData { - symbolTable: SymbolTable; + symbolTable: TargetSymbolTable; vertexMain: ASTNode.FunctionDefinition; fragmentMain: ASTNode.FunctionDefinition; diff --git a/packages/shader-lab/src/parser/ShaderTargetParser.ts b/packages/shader-lab/src/parser/ShaderTargetParser.ts index 2bbb3da72c..15af0c1d91 100644 --- a/packages/shader-lab/src/parser/ShaderTargetParser.ts +++ b/packages/shader-lab/src/parser/ShaderTargetParser.ts @@ -1,5 +1,5 @@ import { Grammar } from "./Grammar"; -import { ENonTerminal, GrammarSymbol } from "./GrammarSymbol"; +import { NoneTerminal, GrammarSymbol } from "./GrammarSymbol"; import { BaseToken } from "../common/BaseToken"; import { ETokenType } from "../common"; import { EAction, StateActionTable, StateGotoTable } from "../lalr/types"; @@ -131,7 +131,7 @@ export class ShaderTargetParser { private _printStack(nextToken: BaseToken) { let str = ""; for (let i = 0; i < this._traceBackStack.length - 1; i++) { - const state = this._traceBackStack[i++]; + const state = this._traceBackStack[i++]; const token = this._traceBackStack[i]; str += `State${state} - ${(token).lexeme ?? ParserUtils.toString(token as GrammarSymbol)}; `; } diff --git a/packages/shader-lab/src/parser/TargetParser.y b/packages/shader-lab/src/parser/TargetParser.y index 17c9b00013..6dd10e5af1 100644 --- a/packages/shader-lab/src/parser/TargetParser.y +++ b/packages/shader-lab/src/parser/TargetParser.y @@ -28,6 +28,8 @@ %token PRECISION %token INVARIANT +%token layout +%token location %token or %token xor @@ -106,9 +108,13 @@ struct_declaration_list: struct_declaration: type_specifier struct_declarator_list ';' - | type_qualifier type_specifier struct_declaration_list ';' + | type_qualifier type_specifier struct_declarator_list ';' + | layout_qualifier type_specifier struct_declarator ';' ; +layout_qualifier: + layout '(' location '=' INT_CONSTANT ')' + struct_declarator_list: struct_declarator | struct_declarator_list ',' struct_declarator diff --git a/packages/shader-lab/src/parser/builtin/variables.ts b/packages/shader-lab/src/parser/builtin/variables.ts index 88724d6d8f..6621610704 100644 --- a/packages/shader-lab/src/parser/builtin/variables.ts +++ b/packages/shader-lab/src/parser/builtin/variables.ts @@ -1,4 +1,4 @@ -import { EKeyword, GalaceanDataType } from "../../common"; +import { EKeyword, GalaceanDataType, TypeAny } from "../../common"; import { EShaderStage } from "../../common/Enums"; export const BuiltinVariableTable: Map = new Map(); @@ -34,6 +34,7 @@ BuiltinVariable.createVariable("gl_FrontFacing", EKeyword.BOOL, EShaderStage.FRA BuiltinVariable.createVariable("gl_FragDepth", EKeyword.FLOAT, EShaderStage.FRAGMENT); BuiltinVariable.createVariable("gl_PointCoord", EKeyword.VEC2, EShaderStage.FRAGMENT); BuiltinVariable.createVariable("gl_FragColor", EKeyword.VEC4, EShaderStage.FRAGMENT); +BuiltinVariable.createVariable("gl_FragData", EKeyword.VEC4_ARRAY, EShaderStage.FRAGMENT); BuiltinVariable.createVariable("gl_MaxVertexAttribs", EKeyword.INT); BuiltinVariable.createVariable("gl_MaxVertexUniformVectors", EKeyword.INT); diff --git a/packages/shader-lab/src/parser/symbolTable/SymbolInfo.ts b/packages/shader-lab/src/parser/symbolTable/SymbolInfo.ts index b58bbdb327..e7c4152d47 100644 --- a/packages/shader-lab/src/parser/symbolTable/SymbolInfo.ts +++ b/packages/shader-lab/src/parser/symbolTable/SymbolInfo.ts @@ -1,4 +1,5 @@ import { IBaseSymbol } from "../../common/BaseSymbolTable"; +import { GalaceanDataType } from "../../common/types"; import { ASTNode } from "../AST"; import { SymbolDataType } from "./SymbolDataType"; @@ -8,7 +9,7 @@ export enum ESymbolType { STRUCT } -type SymbolAstNode = +export type SymbolAstNode = | ASTNode.Initializer | ASTNode.StructSpecifier | ASTNode.FunctionDefinition @@ -21,6 +22,7 @@ export class SymbolInfo implements IBaseSymbol { public readonly ident: string, public readonly symbolType: ESymbolType, public readonly astNode?: SymbolAstNode, - public readonly dataType?: SymbolDataType + public readonly dataType?: SymbolDataType, + public readonly signature?: GalaceanDataType[] ) {} } diff --git a/packages/shader-lab/src/parser/symbolTable/SymbolTable.ts b/packages/shader-lab/src/parser/symbolTable/SymbolTable.ts deleted file mode 100644 index 6908d6874b..0000000000 --- a/packages/shader-lab/src/parser/symbolTable/SymbolTable.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { GalaceanDataType, TypeAny } from "../../common"; -import { BaseSymbolTable } from "../../common/BaseSymbolTable"; -import { ASTNode } from "../AST"; -import { FnSymbol } from "./FnSymbol"; -import { ESymbolType, SymbolInfo } from "./SymbolInfo"; - -export class SymbolTable extends BaseSymbolTable { - override symbolEqualCheck(exist: SymbolInfo, newSymbol: SymbolInfo & { signature?: GalaceanDataType[] }): boolean { - if (exist.symbolType !== newSymbol.symbolType) return false; - if (newSymbol.symbolType === ESymbolType.FN) { - if (!newSymbol.astNode && !newSymbol.signature) return true; - - const existParams = (exist.astNode as ASTNode.FunctionDefinition).protoType.paramSig; - const newSymbolParams = - newSymbol.signature ?? (newSymbol.astNode as ASTNode.FunctionDefinition).protoType.paramSig; - if (existParams.length !== newSymbolParams.length) return false; - for (let i = 0; i < existParams.length; i++) { - if (existParams[i] === TypeAny || newSymbolParams[i] === TypeAny) continue; - if (existParams[i] !== newSymbolParams[i]) return false; - } - } - return true; - } - - getAllFnSymbols(fnIdent: string): FnSymbol[] { - const entries = this._table.get(fnIdent) ?? []; - return entries.filter((item) => item.symbolType === ESymbolType.FN) as FnSymbol[]; - } -} diff --git a/packages/shader-lab/src/parser/symbolTable/TargetSymbolTable.ts b/packages/shader-lab/src/parser/symbolTable/TargetSymbolTable.ts new file mode 100644 index 0000000000..729f6a7020 --- /dev/null +++ b/packages/shader-lab/src/parser/symbolTable/TargetSymbolTable.ts @@ -0,0 +1,74 @@ +import { Logger } from "@galacean/engine"; +import { GalaceanDataType, TypeAny } from "../../common"; +import { BaseSymbolTable } from "../../common/BaseSymbolTable"; +import { ASTNode } from "../AST"; +import { FnSymbol } from "./FnSymbol"; +import { StructSymbol } from "./StructSymbol"; +import { ESymbolType, SymbolAstNode, SymbolInfo } from "./SymbolInfo"; +import { VarSymbol } from "./VarSymbol"; + +export class TargetSymbolTable extends BaseSymbolTable { + override insert(sm: SymbolInfo): void { + const entry = this._table.get(sm.ident) ?? []; + for (let i = 0; i < entry.length; i++) { + if (this._compareWith(entry[i], sm.symbolType, sm.signature, sm.astNode)) { + Logger.warn("replace symbol:", sm.ident); + entry[i] = sm; + return; + } + } + entry.push(sm); + this._table.set(sm.ident, entry); + } + + lookup( + ident: string, + symbolType: T, + signature?: GalaceanDataType[], + astNode?: ASTNode.FunctionDefinition + ): T extends ESymbolType.FN + ? FnSymbol + : T extends ESymbolType.STRUCT + ? StructSymbol + : T extends ESymbolType.VAR + ? VarSymbol + : SymbolInfo { + const entry = this._table.get(ident); + if (entry) { + for (let length = entry.length, i = 0; i < length; i++) { + const item = entry[i]; + if (this._compareWith(item, symbolType, signature, astNode)) return item; + } + } + } + + getAllFnSymbols(fnIdent: string): FnSymbol[] { + const entries = this._table.get(fnIdent) ?? []; + return entries.filter((item) => item.symbolType === ESymbolType.FN) as FnSymbol[]; + } + + private _compareWith( + item: SymbolInfo, + symbolType: ESymbolType, + signature?: GalaceanDataType[], + astNode?: SymbolAstNode + ): boolean { + if (item.symbolType !== symbolType) return false; + if (item.symbolType === ESymbolType.FN) { + if (!astNode && !signature) return true; + + const params = (item.astNode).protoType.paramSig; + const comparedParams = signature ?? (astNode).protoType.paramSig; + const length = params.length; + if (length !== comparedParams.length) return false; + for (let i = 0; i < length; i++) { + const t1 = params[i], + t2 = comparedParams[i]; + if (t1 === TypeAny || t2 === TypeAny) continue; + if (t1 !== t2) return false; + } + return true; + } + return true; + } +} diff --git a/packages/shader-lab/src/parser/symbolTable/index.ts b/packages/shader-lab/src/parser/symbolTable/index.ts index 97193e552e..b4d44fa849 100644 --- a/packages/shader-lab/src/parser/symbolTable/index.ts +++ b/packages/shader-lab/src/parser/symbolTable/index.ts @@ -2,5 +2,5 @@ export * from "./FnSymbol"; export * from "./StructSymbol"; export * from "./SymbolDataType"; export * from "./SymbolInfo"; -export * from "./SymbolTable"; +export * from "./TargetSymbolTable"; export * from "./VarSymbol"; diff --git a/packages/shader-lab/src/parser/types.ts b/packages/shader-lab/src/parser/types.ts index 525b260bdf..f72b0777b1 100644 --- a/packages/shader-lab/src/parser/types.ts +++ b/packages/shader-lab/src/parser/types.ts @@ -1,33 +1,26 @@ -import { ENonTerminal } from "./GrammarSymbol"; +import { NoneTerminal } from "./GrammarSymbol"; import { BaseToken } from "../common/BaseToken"; import { GalaceanDataType, ShaderRange } from "../common"; import { ASTNode, TreeNode } from "./AST"; -export type TraceStackItem = ENonTerminal | BaseToken; +export type TraceStackItem = NoneTerminal | BaseToken; export class SymbolType { - type: GalaceanDataType; - arraySpecifier?: ASTNode.ArraySpecifier; - typeLexeme: string; - - constructor(type: GalaceanDataType, typeLexeme: string, arraySpecifier?: ASTNode.ArraySpecifier) { - this.type = type; - this.arraySpecifier = arraySpecifier; - this.typeLexeme = typeLexeme; - } + constructor( + public type: GalaceanDataType, + public typeLexeme: string, + public arraySpecifier?: ASTNode.ArraySpecifier + ) {} } export class StructProp implements IParamInfo { - typeInfo: SymbolType; - ident: BaseToken; - astNode: ASTNode.StructDeclarator; - - constructor(type: SymbolType, ident: BaseToken) { - this.typeInfo = type; - this.ident = ident; - } + constructor( + public typeInfo: SymbolType, + public ident: BaseToken, + public mrtIndex?: number + ) {} } export type NodeChild = TreeNode | BaseToken; -export type IParamInfo = { ident: BaseToken; typeInfo: SymbolType; astNode: TreeNode }; +export type IParamInfo = { ident: BaseToken; typeInfo: SymbolType; astNode?: TreeNode }; diff --git a/tests/src/shader-lab/ShaderLab.test.ts b/tests/src/shader-lab/ShaderLab.test.ts index 08b367ba52..3544e0f2fe 100644 --- a/tests/src/shader-lab/ShaderLab.test.ts +++ b/tests/src/shader-lab/ShaderLab.test.ts @@ -1,4 +1,10 @@ -import { BlendOperation, CompareFunction, CullMode, RenderStateDataKey } from "@galacean/engine-core"; +import { + BlendOperation, + CompareFunction, + CullMode, + RenderStateDataKey, + ShaderPlatformTarget +} from "@galacean/engine-core"; import { Color } from "@galacean/engine-math"; import { ShaderLab as ShaderLabVerbose, GSError } from "@galacean/engine-shader-lab/verbose"; import { ShaderLab as ShaderLabRelease } from "@galacean/engine-shader-lab"; @@ -256,4 +262,25 @@ describe("ShaderLab", () => { console.log(err.toString()); } }); + + it("mrt-normal", async () => { + const shaderSource = await readFile("./shaders/mrt-normal.shader"); + glslValidate(shaderSource, shaderLabVerbose, {}); + glslValidate(shaderSource, shaderLabRelease, {}); + }); + + it("mrt-struct", async () => { + const shaderSource = await readFile("./shaders/mrt-struct.shader"); + glslValidate(shaderSource, shaderLabVerbose, {}); + glslValidate(shaderSource, shaderLabVerbose, {}); + }); + + it("mrt-error1", async () => { + const shaderSource = await readFile("./shaders/mrt-error1.shader"); + shaderParse.bind(shaderLabVerbose)(shaderSource, [], ShaderPlatformTarget.GLES300); + const errors = shaderLabVerbose.errors; + expect(errors.length).to.eq(1); + expect(errors[0]).to.be.a.instanceOf(GSError); + expect(errors[0].toString()).include("cannot use both gl_FragData and gl_FragColor"); + }); }); diff --git a/tests/src/shader-lab/shaders/mrt-error1.shader b/tests/src/shader-lab/shaders/mrt-error1.shader new file mode 100644 index 0000000000..7fe361fd86 --- /dev/null +++ b/tests/src/shader-lab/shaders/mrt-error1.shader @@ -0,0 +1,37 @@ +Shader "Triangle" { + SubShader "Default" { + Pass "0" { + mat4 renderer_MVPMat; + vec3 u_color; + + struct a2v { + vec4 POSITION; + }; + + struct v2f { + vec3 v_color; + }; + + v2f vert(a2v v) { + v2f o; + + gl_Position = renderer_MVPMat * v.POSITION; + o.v_color = u_color; + return o; + } + + struct MRT { + layout(location = 0) vec4 fragColor0; + layout(location = 1) vec4 fragColor1; + }; + + void frag(v2f i) { + gl_FragColor = vec4(1.); + gl_FragData[0] = vec4(0.3); + } + + VertexShader = vert; + FragmentShader = frag; + } + } +} \ No newline at end of file diff --git a/tests/src/shader-lab/shaders/mrt-normal.shader b/tests/src/shader-lab/shaders/mrt-normal.shader new file mode 100644 index 0000000000..ac1aac40e5 --- /dev/null +++ b/tests/src/shader-lab/shaders/mrt-normal.shader @@ -0,0 +1,42 @@ +Shader "/custom.gs" { + + SubShader "Default" { + + Pass "Pass0" { + struct Attributes { + vec3 POSITION; + vec2 TEXCOORD_0; + vec4 JOINTS_0; + vec4 WEIGHTS_0; + }; + + struct Varyings { + vec2 uv; + }; + + vec4 material_BaseColor; + mat4 renderer_MVPMat; + + VertexShader = vert; + FragmentShader = frag; + + Varyings vert(Attributes attr) { + Varyings v; + + vec4 position = vec4(attr.POSITION, 1.0); + + gl_Position = renderer_MVPMat * position; + v.uv = attr.TEXCOORD_0; + + return v; + } + + void frag(Varyings v) { + vec4 baseColor = material_BaseColor; + + gl_FragData[0] = baseColor; + gl_FragData[1] = baseColor; + } + } + } +} \ No newline at end of file diff --git a/tests/src/shader-lab/shaders/mrt-struct.shader b/tests/src/shader-lab/shaders/mrt-struct.shader new file mode 100644 index 0000000000..7f3886ecde --- /dev/null +++ b/tests/src/shader-lab/shaders/mrt-struct.shader @@ -0,0 +1,50 @@ +Shader "/custom.gs" { + + SubShader "Default" { + + Pass "Pass0" { + struct Attributes { + vec3 POSITION; + vec2 TEXCOORD_0; + vec4 JOINTS_0; + vec4 WEIGHTS_0; + }; + + struct Varyings { + vec2 uv; + }; + + vec4 material_BaseColor; + mat4 renderer_MVPMat; + + VertexShader = vert; + FragmentShader = frag; + + Varyings vert(Attributes attr) { + Varyings v; + + vec4 position = vec4(attr.POSITION, 1.0); + + gl_Position = renderer_MVPMat * position; + v.uv = attr.TEXCOORD_0; + + return v; + } + + struct mrt { + layout(location = 0) vec4 fragColor0; + layout(location = 1) vec4 fragColor1; + }; + + mrt frag(Varyings v) { + mrt o; + + vec4 baseColor = material_BaseColor; + + o.fragColor0 = baseColor; + o.fragColor1 = baseColor; + return o; + } + } + } +} \ No newline at end of file diff --git a/tests/vitest.config.ts b/tests/vitest.config.ts index 7014792762..6a87b0e5b7 100644 --- a/tests/vitest.config.ts +++ b/tests/vitest.config.ts @@ -19,7 +19,8 @@ export default defineProject({ launch: { args: ["--use-gl=egl", "--ignore-gpu-blocklist", "--use-gl=angle"] } - } + }, + headless: true } } });