Improve ShaderLab compilation error log and package build (#2364)

* feat(shaderlab): [wip] optmize error log

* feat(shaderlab): multi package

* feat: add shaderlab verbose package

* feat: update readme

* fix: new scope when parse pass
This commit is contained in:
SwayYan
2024-10-16 17:41:27 +08:00
committed by GitHub
parent dbd17cf568
commit ab1ceb3bd7
47 changed files with 1022 additions and 583 deletions

View File

@@ -50,7 +50,7 @@ jobs:
- name: Install
run: pnpm install
- run: npm run build:editor
- run: npm run build
codecov:
runs-on: macos-latest
@@ -66,7 +66,7 @@ jobs:
- name: Install
run: pnpm install
- name: Build
run: npm run build:editor
run: npm run build
- name: Test
run: npm run test-cov
- name: Upload coverage to Codecov
@@ -96,7 +96,7 @@ jobs:
- name: Run Cypress Tests
uses: cypress-io/github-action@v5
with:
build: npm run build:editor
build: npm run build
start: npm run e2e:case
wait-on: "http://localhost:5175"
wait-on-timeout: 120

View File

@@ -9,19 +9,14 @@
"test-debug": "cross-env TS_NODE_PROJECT=tsconfig.tests.json floss --path tests -r ts-node/register --debug",
"test-cov": "cross-env TS_NODE_PROJECT=tsconfig.tests.json IS_COV=1 nyc --reporter=lcov floss --path tests -r ts-node/register",
"build": "npm run b:module && npm run b:types",
"build:editor": "npm run b:module:editor && npm run b:types",
"lint": "eslint packages/*/src --ext .ts",
"watch": "cross-env NODE_ENV=release BUILD_TYPE=MODULE rollup -cw -m inline",
"watch:editor": "cross-env NODE_ENV=editor BUILD_TYPE=MODULE rollup -cw -m inline",
"watch:umd": "cross-env NODE_ENV=release BUILD_TYPE=UMD rollup -cw -m inline",
"watch:umd:editor": "cross-env NODE_ENV=editor BUILD_TYPE=UMD rollup -cw -m inline",
"b:types": "pnpm -r --filter=./packages/* run b:types",
"b:module": "cross-env BUILD_TYPE=MODULE NODE_ENV=release rollup -c",
"b:module:editor": "cross-env BUILD_TYPE=MODULE NODE_ENV=editor rollup -c",
"b:umd": "cross-env BUILD_TYPE=UMD NODE_ENV=release rollup -c",
"b:miniprogram": "cross-env BUILD_TYPE=MINI rollup -c",
"b:all": "cross-env NODE_ENV=release npm run b:types && cross-env BUILD_TYPE=ALL NODE_ENV=release rollup -c",
"b:all:editor": "cross-env NODE_ENV=editor npm run b:types && cross-env BUILD_TYPE=ALL rollup -c",
"clean": "pnpm -r exec rm -rf dist && pnpm -r exec rm -rf types",
"e2e:case": "pnpm -C ./e2e run case",
"e2e": "cypress run --browser chrome --headless",

View File

@@ -180,7 +180,7 @@ export class ShaderPass extends ShaderPart {
}
const start = performance.now();
const { vertex, fragment } = Shader._shaderLab._parseShaderPass(
const shaderProgramSource = Shader._shaderLab._parseShaderPass(
this._shaderLabSource,
vertexEntry,
fragmentEntry,
@@ -191,7 +191,11 @@ export class ShaderPass extends ShaderPart {
);
Logger.info(`[ShaderLab compilation] cost time: ${performance.now() - start}ms`);
return new ShaderProgram(engine, vertex, fragment);
if (shaderProgramSource) {
return new ShaderProgram(engine, shaderProgramSource.vertex, shaderProgramSource.fragment);
} else {
return new ShaderProgram(engine, "", "");
}
}
// TODO: remove it after migrate all shader to `ShaderLab`.

View File

@@ -26,6 +26,14 @@ const shader = Shader.create(galaceanShaderCode);
engine.run()
```
There are two versions of ShaderLab: `Release` and `Verbose`. The `Verbose` version offers more user-friendly diagnostic information for debug ShaderLab compilation errors, while the Release version provides superior performance.
you can use `Verbose` version by import:
```ts
import { ShaderLab } from "@galacean/engine-shader-lab/verbose";
```
## CFG Grammar conflict detection
The Galacean ShaderLab syntax is defined using Context-Free Grammar (CFG) and is documented within the `\*.y` file. When modifications to the ShaderLab syntax are required, it is recommended to make changes to the existing CFG syntax file, and employ [Bison](https://www.gnu.org/software/bison/manual/bison.html) to detect any potential grammar conflicts.

View File

@@ -25,7 +25,8 @@
},
"files": [
"dist/**/*",
"types/**/*"
"types/**/*",
"verbose/package.json"
],
"devDependencies": {
"@galacean/engine-design": "workspace:*",

View File

@@ -1,19 +0,0 @@
// #if _EDITOR
import { ShaderRange } from "./common";
export abstract class GSError extends Error {
readonly loc: ShaderRange;
constructor(message: string, loc: ShaderRange, cause?: Error) {
super(message, { cause });
this.loc = loc;
}
}
export class SemanticError extends GSError {
constructor(message: string, loc: ShaderRange, cause?: Error) {
super(message, loc, cause);
this.name = "SemanticError";
}
}
// #endif

View File

@@ -0,0 +1,67 @@
// #if _VERBOSE
import { ShaderPosition } from "./common/ShaderPosition";
import { ShaderRange } from "./common/ShaderRange";
export class GSError extends Error {
static wrappingLineCount = 2;
constructor(
name: GSErrorName,
message: string,
public readonly location: ShaderRange | ShaderPosition,
public readonly source: string,
public readonly file?: string
) {
super(message);
this.name = name;
}
override toString(): string {
let start: ShaderPosition, end: ShaderPosition;
const { message, location, source } = this;
if (!source) {
return message;
}
if (location instanceof ShaderPosition) {
start = end = location;
} else {
start = location.start;
end = location.end;
}
const lines = source.split("\n");
let diagnosticMessage = `${this.name}: ${message}\n\n`;
const lineSplit = "|···";
const wrappingLineCount = GSError.wrappingLineCount;
for (let i = start.line - wrappingLineCount, n = end.line + wrappingLineCount; i <= n; i++) {
const line = lines[i];
diagnosticMessage += lineSplit + `${line}\n`;
if (i < start.line || i > end.line) continue;
let remarkStart = 0;
let remarkEnd = line.length;
let paddingLength = lineSplit.length;
if (i === start.line) {
remarkStart = start.column;
paddingLength += start.column;
} else if (i === end.line) {
remarkEnd = end.column;
}
const remarkLength = Math.max(remarkEnd - remarkStart, 1);
diagnosticMessage += " ".repeat(paddingLength) + "^".repeat(remarkLength) + "\n";
}
return diagnosticMessage;
}
}
// #endif
export enum GSErrorName {
PreprocessorError = "PreprocessorError",
CompilationError = "CompilationError",
ScannerError = "ScannerError"
}

View File

@@ -1,11 +1,10 @@
import { ENonTerminal, GrammarSymbol } from "./parser/GrammarSymbol";
import { BaseToken as Token } from "./common/BaseToken";
import { EKeyword, ETokenType, GalaceanDataType, ShaderRange, ShaderPosition } from "./common";
import { EKeyword, ETokenType, GalaceanDataType } from "./common";
import { TreeNode } from "./parser/AST";
// #if _EDITOR
// #if _VERBOSE
import State from "./lalr/State";
// #endif
import { Logger } from "@galacean/engine";
export class ParserUtils {
static unwrapNodeByType<T = TreeNode>(node: TreeNode, type: ENonTerminal): T | undefined {
@@ -15,7 +14,7 @@ export class ParserUtils {
return ParserUtils.unwrapNodeByType(child, type);
}
// #if _EDITOR
// #if _VERBOSE
/**
* Check if type `tb` is compatible with type `ta`.
*/
@@ -39,15 +38,10 @@ export class ParserUtils {
return sm < ENonTerminal.START;
}
static throw(pos: ShaderPosition | ShaderRange | number, ...msgs: any[]) {
Logger.error(pos.toString(), ...msgs);
throw msgs.join(" ");
}
/**
* @internal
*/
// #if _EDITOR
// #if _VERBOSE
static printStatePool(logPath: string) {
let output = "";

View File

@@ -2,38 +2,33 @@ import { Lexer } from "./lexer";
import { ShaderTargetParser } from "./parser";
import { Preprocessor } from "./preprocessor";
import { GLES100Visitor, GLES300Visitor } from "./codeGen";
import { IShaderContent, IShaderLab } from "@galacean/engine-design/src/shader-lab";
import { IShaderContent, IShaderLab } from "@galacean/engine-design";
import { ShaderContentParser } from "./contentParser";
// @ts-ignore
import { Logger, ShaderLib, ShaderMacro, ShaderPass, ShaderPlatformTarget } from "@galacean/engine";
import { Logger, ShaderLib, ShaderMacro, ShaderPlatformTarget } from "@galacean/engine";
import { ShaderPosition, ShaderRange } from "./common";
import { ShaderLabObjectPool } from "./ShaderLabObjectPool";
// #if _VERBOSE
import { GSError } from "./GSError";
// #endif
import { PpParser } from "./preprocessor/PpParser";
import { ShaderLabUtils } from "./ShaderLabUtils";
import { IShaderProgramSource } from "@galacean/engine-design/types/shader-lab/IShaderProgramSource";
/** @internal */
export class ShaderLab implements IShaderLab {
/**
* @internal
*/
private static _parser = ShaderTargetParser.create();
/**
* @internal
*/
private static _shaderPositionPool = new ShaderLabObjectPool(ShaderPosition);
/**
* @internal
*/
private static _shaderRangePool = new ShaderLabObjectPool(ShaderRange);
private static _shaderPositionPool = ShaderLabUtils.createObjectPool(ShaderPosition);
private static _shaderRangePool = ShaderLabUtils.createObjectPool(ShaderRange);
static createPosition(
index: number,
// #if _EDITOR
line?: number,
column?: number
// #endif
): ShaderPosition {
// #if _VERBOSE
static _processingPassText?: string;
// #endif
static createPosition(index: number, line?: number, column?: number): ShaderPosition {
const position = this._shaderPositionPool.get();
position.setX(
position.set(
index,
// #if _EDITOR
// #if _VERBOSE
line,
column
// #endif
@@ -43,10 +38,15 @@ export class ShaderLab implements IShaderLab {
static createRange(start: ShaderPosition, end: ShaderPosition): ShaderRange {
const range = this._shaderRangePool.get();
range.setX(start, end);
range.set(start, end);
return range;
}
// #if _VERBOSE
/** Retrieve the compilation errors */
readonly errors: GSError[] = [];
// #endif
_parseShaderPass(
source: string,
vertexEntry: string,
@@ -55,8 +55,7 @@ export class ShaderLab implements IShaderLab {
backend: ShaderPlatformTarget,
platformMacros: string[],
basePathForIncludeKey: string
) {
ShaderLabObjectPool.clearAllShaderLabObjectPool();
): IShaderProgramSource | undefined {
Preprocessor.reset(ShaderLib, basePathForIncludeKey);
for (const macro of macros) {
Preprocessor.addPredefinedMacro(macro.name, macro.value);
@@ -66,68 +65,82 @@ export class ShaderLab implements IShaderLab {
Preprocessor.addPredefinedMacro(platformMacros[i]);
}
// #if _EDITOR
// TODO: index to position
// Logger.convertSourceIndex = Preprocessor.convertSourceIndex.bind(Preprocessor);
// #endif
const preprocessorStart = performance.now();
const ppdContent = Preprocessor.process(source);
// #if _VERBOSE
if (PpParser._errors.length > 0) {
for (const err of PpParser._errors) {
this.errors.push(<GSError>err);
}
this._logErrors();
return undefined;
}
// #endif
Logger.info(`[pass compilation - preprocessor] cost time ${performance.now() - preprocessorStart}ms`);
const lexer = new Lexer(ppdContent);
const tokens = lexer.tokenize();
const program = ShaderLab._parser.parse(tokens);
if (!program) {
return { vertex: "", fragment: "" };
const { _parser: parser } = ShaderLab;
ShaderLab._processingPassText = ppdContent;
const program = parser.parse(tokens);
// #if _VERBOSE
for (const err of parser.errors) {
this.errors.push(err);
}
if (!program) {
this._logErrors();
return undefined;
}
// #endif
const codeGen =
backend === ShaderPlatformTarget.GLES100 ? GLES100Visitor.getVisitor() : GLES300Visitor.getVisitor();
const start = performance.now();
const ret = codeGen.visitShaderProgram(program, vertexEntry, fragmentEntry);
Logger.info(`[CodeGen] cost time: ${performance.now() - start}ms`);
ShaderLab._processingPassText = undefined;
// #if _VERBOSE
for (const err of codeGen.errors) {
this.errors.push(err);
}
this._logErrors();
// #endif
return ret;
}
_parseShaderContent(shaderSource: string): IShaderContent {
ShaderLabUtils.clearAllShaderLabObjectPool();
ShaderContentParser.reset();
return ShaderContentParser.parse(shaderSource);
const ret = ShaderContentParser.parse(shaderSource);
// #if _VERBOSE
this.errors.length = 0;
for (const error of ShaderContentParser._errors) {
this.errors.push(error);
}
// #endif
return ret;
}
// #if _EDITOR
// #if _VERBOSE
/**
* @internal
* For debug
*/
_parse(
shaderSource: string,
macros: ShaderMacro[],
backend: ShaderPlatformTarget
): (ReturnType<ShaderLab["_parseShaderPass"]> & { name: string })[] {
const structInfo = this._parseShaderContent(shaderSource);
const passResult = [] as any;
for (const subShader of structInfo.subShaders) {
for (const pass of subShader.passes) {
if (pass.isUsePass) continue;
const passInfo = this._parseShaderPass(
pass.contents,
pass.vertexEntry,
pass.fragmentEntry,
macros,
backend,
[],
// @ts-ignore
new URL(pass.name, ShaderPass._shaderRootPath).href
) as any;
passInfo.name = pass.name;
passResult.push(passInfo);
}
_logErrors() {
const errors = this.errors;
if (errors.length === 0 || !Logger.isEnabled) return;
Logger.error(`${errors.length} errors occur!`);
for (const err of errors) {
Logger.error(err.toString());
}
return passResult;
}
// #endif
}

View File

@@ -1,18 +0,0 @@
import { ClearableObjectPool, IPoolElement } from "@galacean/engine";
/**
* @internal
*/
export class ShaderLabObjectPool<T extends IPoolElement> extends ClearableObjectPool<T> {
static ShaderLabObjectPoolSet: ShaderLabObjectPool<IPoolElement>[] = [];
static clearAllShaderLabObjectPool() {
for (let i = 0; i < this.ShaderLabObjectPoolSet.length; i++) {
this.ShaderLabObjectPoolSet[i].clear();
}
}
constructor(type: new () => T) {
super(type);
ShaderLabObjectPool.ShaderLabObjectPoolSet.push(this);
}
}

View File

@@ -0,0 +1,37 @@
import { ClearableObjectPool, IPoolElement } from "@galacean/engine";
import { GSErrorName } from "./GSError";
import { ShaderRange } from "./common/ShaderRange";
import { ShaderPosition } from "./common/ShaderPosition";
// #if _VERBOSE
import { GSError } from "./GSError";
// #endif
export class ShaderLabUtils {
private static _shaderLabObjectPoolSet: ClearableObjectPool<IPoolElement>[] = [];
static createObjectPool<T extends IPoolElement>(type: new () => T) {
const pool = new ClearableObjectPool<T>(type);
ShaderLabUtils._shaderLabObjectPoolSet.push(pool);
return pool;
}
static clearAllShaderLabObjectPool() {
for (let i = 0, n = ShaderLabUtils._shaderLabObjectPoolSet.length; i < n; i++) {
ShaderLabUtils._shaderLabObjectPoolSet[i].clear();
}
}
static createGSError(
message: string,
errorName: GSErrorName,
source: string,
location: ShaderRange | ShaderPosition,
file?: string
) {
// #if _VERBOSE
return new GSError(errorName, message, location, source, file);
// #else
throw new Error(`[${errorName}]: ${message}`);
// #endif
}
}

View File

@@ -1,17 +1,25 @@
import { ENonTerminal } from "../parser/GrammarSymbol";
import { BaseToken as Token } from "../common/BaseToken";
import { EKeyword } from "../common";
import { EKeyword, ShaderPosition, ShaderRange } from "../common";
import { ASTNode, TreeNode } from "../parser/AST";
import { ESymbolType, FnSymbol, VarSymbol } from "../parser/symbolTable";
import { ParserUtils } from "../Utils";
import { ParserUtils } from "../ParserUtils";
import { NodeChild } from "../parser/types";
import { VisitorContext } from "./VisitorContext";
import { ShaderLab } from "../ShaderLab";
import { GSErrorName } from "../GSError";
// #if _VERBOSE
import { GSError } from "../GSError";
// #endif
/**
* @internal
* The code generator
*/
export class CodeGenVisitor {
protected constructor() {}
// #if _VERBOSE
readonly errors: GSError[] = [];
// #endif
defaultCodeGen(children: NodeChild[]) {
let ret: string[] = [];
@@ -35,10 +43,20 @@ export class CodeGenVisitor {
if (prop instanceof Token) {
if (context.isAttributeStruct(<string>postExpr.type)) {
context.referenceAttribute(prop.lexeme);
const error = context.referenceAttribute(prop);
// #if _VERBOSE
if (error) {
this.errors.push(error);
}
// #endif
return prop.lexeme;
} else if (context.isVaryingStruct(<string>postExpr.type)) {
context.referenceVarying(prop.lexeme);
const error = context.referenceVarying(prop);
// #if _VERBOSE
if (error) {
this.errors.push(error);
}
// #endif
return prop.lexeme;
}
@@ -172,4 +190,12 @@ export class CodeGenVisitor {
visitFunctionIdentifier(node: ASTNode.FunctionIdentifier): string {
return this.defaultCodeGen(node.children);
}
protected _reportError(loc: ShaderRange | ShaderPosition, message: string): void {
// #if _VERBOSE
this.errors.push(new GSError(GSErrorName.CompilationError, message, loc, ShaderLab._processingPassText));
// #else
throw new Error(message);
// #endif
}
}

View File

@@ -1,4 +1,3 @@
import { CodeGenVisitor } from "./CodeGenVisitor";
import { GLESVisitor } from "./GLESVisitor";
import { VisitorContext } from "./VisitorContext";
import { ICodeSegment } from "./types";

View File

@@ -5,8 +5,8 @@ import { ESymbolType, FnSymbol, StructSymbol, SymbolInfo } from "../parser/symbo
import { EShaderStage } from "../common/Enums";
import { IShaderInfo } from "@galacean/engine-design";
import { ICodeSegment } from "./types";
import { Logger } from "@galacean/engine";
import { VisitorContext } from "./VisitorContext";
import { EKeyword } from "../common";
const defaultPrecision = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
@@ -18,6 +18,9 @@ const defaultPrecision = `
#endif
`;
/**
* @internal
*/
export abstract class GLESVisitor extends CodeGenVisitor {
protected _versionText: string = "";
protected _extensions: string = "";
@@ -26,6 +29,9 @@ export abstract class GLESVisitor extends CodeGenVisitor {
abstract getVaryingDeclare(): ICodeSegment[];
visitShaderProgram(node: ASTNode.GLShaderProgram, vertexEntry: string, fragmentEntry: string): IShaderInfo {
// #if _VERBOSE
this.errors.length = 0;
// #endif
VisitorContext.reset();
VisitorContext.context._passSymbolTable = node.shaderData.symbolTable;
@@ -44,15 +50,15 @@ export abstract class GLESVisitor extends CodeGenVisitor {
VisitorContext.context.stage = EShaderStage.VERTEX;
const returnType = fnNode.protoType.returnType;
if (typeof returnType.type !== "string") {
Logger.warn("main entry can only return struct.");
} else {
if (typeof returnType.type === "string") {
const varyStruct = symbolTable.lookup<StructSymbol>({ ident: returnType.type, symbolType: ESymbolType.STRUCT });
if (!varyStruct) {
Logger.warn("invalid varying struct:", returnType.type);
this._reportError(returnType.location, `invalid varying struct: ${returnType.type}`);
} else {
VisitorContext.context.varyingStruct = varyStruct.astNode;
}
} else if (returnType.type !== EKeyword.VOID) {
this._reportError(returnType.location, "main entry can only return struct.");
}
const paramList = fnNode.protoType.parameterList;
@@ -64,7 +70,7 @@ export abstract class GLESVisitor extends CodeGenVisitor {
symbolType: ESymbolType.STRUCT
});
if (!structSymbol) {
Logger.warn("no attribute struct found.");
this._reportError(paramInfo.astNode.location, `Not found attribute struct "${paramInfo.typeInfo.type}".`);
continue;
}
VisitorContext.context.attributeStructs.push(structSymbol.astNode);

View File

@@ -1,9 +1,16 @@
import { Logger } from "@galacean/engine";
import { EShaderStage } from "../common/Enums";
import { ASTNode } from "../parser/AST";
import { ESymbolType, SymbolTable, SymbolInfo } from "../parser/symbolTable";
import { IParamInfo } from "../parser/types";
import { GSErrorName } from "../GSError";
import { BaseToken } from "../common/BaseToken";
import { ShaderLab } from "../ShaderLab";
import { ShaderLabUtils } from "../ShaderLabUtils";
// #if _VERBOSE
import { GSError } from "../GSError";
// #endif
/** @internal */
export class VisitorContext {
private static _singleton: VisitorContext;
static get context() {
@@ -30,12 +37,13 @@ export class VisitorContext {
_curFn?: ASTNode.FunctionProtoType;
_passSymbolTable: SymbolTable;
private constructor() {}
get passSymbolTable() {
return this._passSymbolTable;
}
private constructor() {}
reset() {
this.attributeList.length = 0;
this.attributeStructs.length = 0;
@@ -52,26 +60,34 @@ export class VisitorContext {
return this.varyingStruct?.ident?.lexeme === type;
}
referenceAttribute(ident: string) {
if (this._referencedAttributeList[ident]) return;
referenceAttribute(ident: BaseToken): GSError {
if (this._referencedAttributeList[ident.lexeme]) return;
const prop = this.attributeList.find((item) => item.ident.lexeme === ident);
const prop = this.attributeList.find((item) => item.ident.lexeme === ident.lexeme);
if (!prop) {
Logger.error("referenced attribute not found:", ident);
return;
return ShaderLabUtils.createGSError(
`referenced attribute not found: ${ident.lexeme}`,
GSErrorName.CompilationError,
ShaderLab._processingPassText,
ident.location
);
}
this._referencedAttributeList[ident] = prop;
this._referencedAttributeList[ident.lexeme] = prop;
}
referenceVarying(ident: string) {
if (this._referencedVaryingList[ident]) return;
referenceVarying(ident: BaseToken): GSError | undefined {
if (this._referencedVaryingList[ident.lexeme]) return;
const prop = this.varyingStruct?.propList.find((item) => item.ident.lexeme === ident);
const prop = this.varyingStruct?.propList.find((item) => item.ident.lexeme === ident.lexeme);
if (!prop) {
Logger.error("referenced varying not found:", ident);
return;
return ShaderLabUtils.createGSError(
`referenced varying not found: ${ident.lexeme}`,
GSErrorName.CompilationError,
ShaderLab._processingPassText,
ident.location
);
}
this._referencedVaryingList[ident] = prop;
this._referencedVaryingList[ident.lexeme] = prop;
}
referenceGlobal(ident: string, type: ESymbolType) {

View File

@@ -1,10 +1,14 @@
import { ETokenType, ShaderRange, ShaderPosition } from ".";
import { GSErrorName } from "../GSError";
import { ShaderLab } from "../ShaderLab";
import { ParserUtils } from "../Utils";
import { BaseToken } from "./BaseToken";
import { ShaderLabUtils } from "../ShaderLabUtils";
export type OnToken = (token: BaseToken, scanner: BaseScanner) => void;
/**
* @internal
*/
export default class BaseScanner {
private static _spaceCharsWithBreak = [" ", "\t", "\n"];
private static _spaceChars = [" ", "\t"];
@@ -21,7 +25,7 @@ export default class BaseScanner {
protected _currentIndex = 0;
protected _source: string;
// #if _EDITOR
// #if _VERBOSE
protected _column = 0;
protected _line = 0;
// #endif
@@ -34,16 +38,26 @@ export default class BaseScanner {
return this._source;
}
get curPosition(): ShaderPosition {
getCurPosition(): ShaderPosition {
return ShaderLab.createPosition(
this._currentIndex,
// #if _EDITOR
this._column,
this._line
// #if _VERBOSE
this._line,
this._column
// #endif
);
}
// #if _VERBOSE
get line() {
return this._line;
}
get column() {
return this._column;
}
// #endif
protected readonly _keywordsMap: Map<string, number>;
constructor(source: string, kws: Map<string, number> = new Map()) {
@@ -65,17 +79,12 @@ export default class BaseScanner {
}
}
/**
* @internal
*/
_advance(): void {
if (this.isEnd()) {
return;
}
this._currentIndex++;
// #if _EDITOR
// #if _VERBOSE
if (this.getCurChar() === "\n") {
this._line += 1;
this._column = 0;
@@ -83,6 +92,8 @@ export default class BaseScanner {
this._column += 1;
}
// #endif
this._currentIndex++;
}
skipSpace(includeLineBreak: boolean): void {
@@ -98,20 +109,20 @@ export default class BaseScanner {
skipCommentsAndSpace(): ShaderRange | undefined {
this.skipSpace(true);
if (this.peek(2) === "//") {
const start = this.curPosition;
const start = this.getCurPosition();
this.advance(2);
// single line comments
while (this.getCurChar() !== "\n") this._advance();
this.skipCommentsAndSpace();
return ShaderLab.createRange(start, this.curPosition);
return ShaderLab.createRange(start, this.getCurPosition());
} else if (this.peek(2) === "/*") {
const start = this.curPosition;
const start = this.getCurPosition();
this.advance(2);
// multi-line comments
while (this.peek(2) !== "*/" && !this.isEnd()) this._advance();
this.advance(2);
this.skipCommentsAndSpace();
return ShaderLab.createRange(start, this.curPosition);
return ShaderLab.createRange(start, this.getCurPosition());
}
}
@@ -124,11 +135,16 @@ export default class BaseScanner {
this.skipCommentsAndSpace();
const peek = this.peek(text.length);
if (peek !== text) {
ParserUtils.throw(this._currentIndex, `Expect ${text}, got ${peek}`);
this.throwError(this.getCurPosition(), `Expect text "${text}", but got "${peek}"`);
}
this.advance(text.length);
}
throwError(pos: ShaderPosition | ShaderRange, ...msgs: any[]) {
const error = ShaderLabUtils.createGSError(msgs.join(" "), GSErrorName.ScannerError, this._source, pos);
throw error;
}
scanPairedText(left: string, right: string, balanced = false, skipLeading = false) {
if (!skipLeading) {
this.scanText(left);
@@ -157,10 +173,10 @@ export default class BaseScanner {
scanToken(onToken?: OnToken, splitCharRegex = /\w/) {
this.skipCommentsAndSpace();
const start = this.curPosition;
const start = this.getCurPosition();
if (this.isEnd()) return;
while (splitCharRegex.test(this.getCurChar()) && !this.isEnd()) this._advance();
const end = this.curPosition;
const end = this.getCurPosition();
if (start.index === end.index) {
this._advance();

View File

@@ -1,11 +1,11 @@
import { ETokenType } from "./types";
import { ShaderRange, ShaderPosition } from ".";
import { ShaderLab } from "../ShaderLab";
import { ShaderLabObjectPool } from "../ShaderLabObjectPool";
import { IPoolElement } from "@galacean/engine";
import { ShaderLabUtils } from "../ShaderLabUtils";
export class BaseToken<T extends number = number> implements IPoolElement {
static pool = new ShaderLabObjectPool<BaseToken>(BaseToken);
static pool = ShaderLabUtils.createObjectPool(BaseToken);
type: T;
lexeme: string;
@@ -22,9 +22,9 @@ export class BaseToken<T extends number = number> implements IPoolElement {
} else {
const end = ShaderLab.createPosition(
arg.index + lexeme.length,
// #if _EDITOR
// #if _VERBOSE
arg.line,
arg.column
arg.column + lexeme.length
// #endif
);
this.location = ShaderLab.createRange(arg, end);

View File

@@ -2,28 +2,30 @@ import { IPoolElement } from "@galacean/engine";
export class ShaderPosition implements IPoolElement {
index: number;
// #if _EDITOR
line?: number;
column?: number;
// #if _VERBOSE
line: number;
column: number;
// #endif
setX(
set(
index: number,
/** #if _EDITOR */
line?: number,
column?: number
/** #endif */
// #if _VERBOSE
line: number,
column: number
// #endif
) {
this.index = index;
/** #if _EDITOR */
// #if _VERBOSE
this.line = line;
this.column = column;
/** #endif */
// #endif
}
dispose(): void {
this.index = 0;
// #if _VERBOSE
this.line = 0;
this.column = 0;
// #endif
}
}

View File

@@ -5,7 +5,7 @@ export class ShaderRange implements IPoolElement {
public start: ShaderPosition;
public end: ShaderPosition;
setX(start: ShaderPosition, end: ShaderPosition) {
set(start: ShaderPosition, end: ShaderPosition) {
this.start = start;
this.end = end;
}

View File

@@ -25,4 +25,13 @@ export default class Scanner extends BaseScanner {
}
return Number(this._source.substring(start, this._currentIndex));
}
// #if _VERBOSE
scanToCharacter(char: string): void {
while (this.getCurChar() !== char && !this.isEnd()) {
this._advance();
}
this._advance();
}
// #endif
}

View File

@@ -23,7 +23,11 @@ import {
IShaderPassContent,
IRenderStates
} from "@galacean/engine-design";
import { ParserUtils } from "../Utils";
import { GSErrorName } from "../GSError";
// #if _VERBOSE
import { GSError } from "../GSError";
import { ShaderLabUtils } from "../ShaderLabUtils";
// #endif
const EngineType = [
EKeyword.GS_RenderQueueType,
@@ -44,8 +48,14 @@ const RenderStateType = [
EKeyword.GS_StencilState
];
/**
* @internal
*/
export class ShaderContentParser {
static _engineType = { RenderQueueType, CompareFunction, StencilOperation, BlendOperation, BlendFactor, CullMode };
static _errors: GSError[] = [];
private static _isRenderStateDeclarator(token: BaseToken) {
return RenderStateType.includes(token.type);
}
@@ -57,6 +67,7 @@ export class ShaderContentParser {
private static _symbolTable: SymbolTableStack<ISymbol, SymbolTable> = new SymbolTableStack();
static reset() {
this._errors.length = 0;
this._symbolTable.clear();
this._newScope();
}
@@ -88,7 +99,6 @@ export class ShaderContentParser {
for (let i = 0; i < subShader.passes.length; i++) {
const pass = subShader.passes[i];
// for (const pass of subShader.passes) {
Object.assign(pass.renderStates.constantMap, constMap);
Object.assign(pass.renderStates.variableMap, variableMap);
if (pass.isUsePass) continue;
@@ -105,7 +115,7 @@ export class ShaderContentParser {
private static _parseShaderStatements(ret: IShaderContent, scanner: Scanner) {
let braceLevel = 1;
let start = scanner.curPosition;
let start = scanner.getCurPosition();
while (true) {
const word = scanner.scanToken();
@@ -114,20 +124,20 @@ export class ShaderContentParser {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
const subShader = this._parseSubShader(scanner);
ret.subShaders.push(subShader);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case EKeyword.GS_EditorProperties:
case EKeyword.GS_EditorMacros:
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
scanner.scanPairedText("{", "}", true);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case EKeyword.GS_RenderQueueType:
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseRenderQueueAssignment(ret, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case ETokenType.NOT_WORD:
@@ -145,12 +155,12 @@ export class ShaderContentParser {
if (ShaderContentParser._isRenderStateDeclarator(word)) {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseRenderStateDeclarationOrAssignment(ret, word, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
} else if (ShaderContentParser._isEngineType(word)) {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseVariableDeclaration(word.type, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
}
}
@@ -174,7 +184,16 @@ export class ShaderContentParser {
scanner.scanText(";");
const sm = this._symbolTable.lookup({ type: stateToken.type, ident: variable.lexeme });
if (!sm?.value) {
ParserUtils.throw(scanner.current, `Invalid ${stateToken.lexeme} variable:`, variable.lexeme);
const error = ShaderLabUtils.createGSError(
`Invalid "${stateToken.lexeme}" variable: ${variable.lexeme}`,
GSErrorName.CompilationError,
scanner.source,
variable.location
);
// #if _VERBOSE
this._errors.push(error);
return;
// #endif
}
const renderState = sm.value as IRenderStates;
Object.assign(ret.renderStates.constantMap, renderState.constantMap);
@@ -222,15 +241,36 @@ export class ShaderContentParser {
scanner.scanText("]");
scanner.scanText("=");
} else if (op.lexeme !== "=") {
ParserUtils.throw(scanner.current, "Invalid syntax, expect character '=', but got", op.lexeme);
const error = ShaderLabUtils.createGSError(
`Invalid syntax, expect character '=', but got ${op.lexeme}`,
GSErrorName.CompilationError,
scanner.source,
scanner.getCurPosition()
);
// #if _VERBOSE
this._errors.push(error);
scanner.scanToCharacter(";");
return;
// #endif
}
renderStateProp += idx;
}
renderStateProp = state + renderStateProp;
const renderStateElementKey = RenderStateDataKey[renderStateProp];
if (renderStateElementKey == undefined)
ParserUtils.throw(scanner.current, "Invalid render state element", renderStateProp);
if (renderStateElementKey == undefined) {
const error = ShaderLabUtils.createGSError(
`Invalid render state element ${renderStateProp}`,
GSErrorName.CompilationError,
scanner.source,
scanner.getCurPosition()
);
// #if _VERBOSE
this._errors.push(error);
scanner.scanToCharacter(";");
return;
// #endif
}
scanner.skipCommentsAndSpace();
let value: any;
@@ -258,8 +298,19 @@ export class ShaderContentParser {
scanner._advance();
const engineTypeProp = scanner.scanToken();
value = ShaderContentParser._engineType[token.lexeme]?.[engineTypeProp.lexeme];
if (value == undefined)
ParserUtils.throw(scanner.current, "Invalid engine constant:", `${token.lexeme}.${engineTypeProp.lexeme}`);
if (value == undefined) {
const error = ShaderLabUtils.createGSError(
`Invalid engine constant: ${token.lexeme}.${engineTypeProp.lexeme}`,
GSErrorName.CompilationError,
scanner.source,
engineTypeProp.location
);
// #if _VERBOSE
this._errors.push(error);
scanner.scanToCharacter(";");
return;
// #endif
}
} else {
value = token.lexeme;
}
@@ -278,7 +329,16 @@ export class ShaderContentParser {
scanner.scanText(";");
const value = ShaderContentParser._engineType.RenderQueueType[word.lexeme];
if (value == undefined) {
ParserUtils.throw(scanner.current, "Invalid render queue", word.lexeme);
const error = ShaderLabUtils.createGSError(
`Invalid render queue ${word.lexeme}`,
GSErrorName.CompilationError,
scanner.source,
word.location
);
// #if _VERBOSE
this._errors.push(error);
return;
// #endif
}
const key = RenderStateDataKey.RenderQueueType;
ret.renderStates.constantMap[key] = value;
@@ -292,7 +352,7 @@ export class ShaderContentParser {
) {
if (scanner.current > start.index + offset) {
ret.globalContents.push({
range: { start, end: { ...scanner.curPosition, index: scanner.current - offset - 1 } },
range: { start, end: { ...scanner.getCurPosition(), index: scanner.current - offset - 1 } },
content: scanner.source.substring(start.index, scanner.current - offset - 1)
});
}
@@ -311,7 +371,7 @@ export class ShaderContentParser {
scanner.scanText("{");
scanner.skipCommentsAndSpace();
let start = scanner.curPosition;
let start = scanner.getCurPosition();
while (true) {
const word = scanner.scanToken();
@@ -320,13 +380,13 @@ export class ShaderContentParser {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
const pass = this._parsePass(scanner);
ret.passes.push(pass);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case EKeyword.GS_RenderQueueType:
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseRenderQueueAssignment(ret, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case EKeyword.GS_UsePass:
@@ -334,13 +394,13 @@ export class ShaderContentParser {
const name = scanner.scanPairedText('"', '"');
// @ts-ignore
ret.passes.push({ name, isUsePass: true, renderStates: { constantMap: {}, variableMap: {} }, tags: {} });
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case EKeyword.GS_Tags:
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseTags(ret, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case ETokenType.NOT_WORD:
@@ -358,12 +418,12 @@ export class ShaderContentParser {
if (ShaderContentParser._isRenderStateDeclarator(word)) {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseRenderStateDeclarationOrAssignment(ret, word, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
} else if (ShaderContentParser._isEngineType(word)) {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseVariableDeclaration(word.type, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
}
}
@@ -389,6 +449,7 @@ export class ShaderContentParser {
}
private static _parsePass(scanner: Scanner): IShaderPassContent {
this._newScope();
const ret = {
globalContents: [],
renderStates: { constantMap: {}, variableMap: {} },
@@ -401,7 +462,7 @@ export class ShaderContentParser {
let braceLevel = 1;
scanner.skipCommentsAndSpace();
let start = scanner.curPosition;
let start = scanner.getCurPosition();
while (true) {
const word = scanner.scanToken();
@@ -409,13 +470,13 @@ export class ShaderContentParser {
case EKeyword.GS_RenderQueueType:
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseRenderQueueAssignment(ret, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case EKeyword.GS_Tags:
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseTags(ret, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case EKeyword.GS_VertexShader:
@@ -423,15 +484,22 @@ export class ShaderContentParser {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
scanner.scanText("=");
const entry = scanner.scanToken();
// #if _EDITOR
if (ret[word.lexeme]) {
ParserUtils.throw(scanner.current, "reassign main entry");
const error = ShaderLabUtils.createGSError(
"reassign main entry",
GSErrorName.CompilationError,
scanner.source,
scanner.getCurPosition()
);
// #if _VERBOSE
Logger.error(error.toString());
throw error;
// #endif
}
// #endif
const key = word.type === EKeyword.GS_VertexShader ? "vertexEntry" : "fragmentEntry";
ret[key] = entry.lexeme;
scanner.scanText(";");
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
case ETokenType.NOT_WORD:
@@ -449,12 +517,12 @@ export class ShaderContentParser {
if (ShaderContentParser._isRenderStateDeclarator(word)) {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseRenderStateDeclarationOrAssignment(ret, word, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
} else if (ShaderContentParser._isEngineType(word)) {
this._addGlobalStatement(ret, scanner, start, word.lexeme.length);
this._parseVariableDeclaration(word.type, scanner);
start = scanner.curPosition;
start = scanner.getCurPosition();
break;
}
}

View File

@@ -1,15 +1,16 @@
export { ShaderLab } from "./ShaderLab";
// #if _EDITOR
// #if _VERBOSE
export { Preprocessor } from "./preprocessor";
export * from "./GSError";
// #endif
//@ts-ignore
export const version = `__buildVersion`;
let mode = "Release";
// #if _EDITOR
mode = "Editor";
// #if _VERBOSE
mode = "Verbose";
// #endif
console.log(`Galacean ShaderLab version: ${version}. mode: ${mode}`);

View File

@@ -121,7 +121,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.storage_qualifier,
[[EKeyword.CONST], [EKeyword.IN], [EKeyword.INOUT], [EKeyword.OUT], [EKeyword.CENTROID]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.StorageQualifier.pool
// #endif
),
@@ -129,7 +129,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.interpolation_qualifier,
[[EKeyword.SMOOTH], [EKeyword.FLAT]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.InterpolationQualifier.pool
// #endif
),
@@ -137,7 +137,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.invariant_qualifier,
[[EKeyword.INVARIANT]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.InvariantQualifier.pool
// #endif
),
@@ -145,7 +145,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.precision_qualifier,
[[EKeyword.HIGHP], [EKeyword.MEDIUMP], [EKeyword.LOWP]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.PrecisionQualifier.pool
// #endif
),
@@ -253,7 +253,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
ENonTerminal.assignment_expression
]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.ConditionalExpression.pool
// #endif
),
@@ -264,7 +264,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.logical_xor_expression],
[ENonTerminal.logical_or_expression, ETokenType.OR_OP, ENonTerminal.logical_xor_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.LogicalOrExpression.pool
// #endif
),
@@ -275,7 +275,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.logical_and_expression],
[ENonTerminal.logical_xor_expression, ETokenType.XOR_OP, ENonTerminal.logical_and_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.LogicalXorExpression.pool
// #endif
),
@@ -286,7 +286,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.inclusive_or_expression],
[ENonTerminal.logical_and_expression, ETokenType.AND_OP, ENonTerminal.inclusive_or_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.LogicalAndExpression.pool
// #endif
),
@@ -297,7 +297,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.exclusive_or_expression],
[ENonTerminal.inclusive_or_expression, ETokenType.VERTICAL_BAR, ENonTerminal.exclusive_or_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.InclusiveOrExpression.pool
// #endif
),
@@ -308,7 +308,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.and_expression],
[ENonTerminal.exclusive_or_expression, ETokenType.CARET, ENonTerminal.and_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.ExclusiveOrExpression.pool
// #endif
),
@@ -319,7 +319,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.equality_expression],
[ENonTerminal.and_expression, ETokenType.AMPERSAND, ENonTerminal.equality_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.AndExpression.pool
// #endif
),
@@ -331,7 +331,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.equality_expression, ETokenType.EQ_OP, ENonTerminal.relational_expression],
[ENonTerminal.equality_expression, ETokenType.NE_OP, ENonTerminal.relational_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.EqualityExpression.pool
// #endif
),
@@ -345,7 +345,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.relational_expression, ETokenType.LE_OP, ENonTerminal.shift_expression],
[ENonTerminal.relational_expression, ETokenType.GE_OP, ENonTerminal.shift_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.RelationalExpression.pool
// #endif
),
@@ -357,7 +357,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.shift_expression, ETokenType.LEFT_OP, ENonTerminal.additive_expression],
[ENonTerminal.shift_expression, ETokenType.RIGHT_OP, ENonTerminal.additive_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.ShiftExpression.pool
// #endif
),
@@ -369,7 +369,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.additive_expression, ETokenType.PLUS, ENonTerminal.multiplicative_expression],
[ENonTerminal.additive_expression, ETokenType.DASH, ENonTerminal.multiplicative_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.AdditiveExpression.pool
// #endif
),
@@ -382,7 +382,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.multiplicative_expression, ETokenType.SLASH, ENonTerminal.unary_expression],
[ENonTerminal.multiplicative_expression, ETokenType.PERCENT, ENonTerminal.unary_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.MultiplicativeExpression.pool
// #endif
),
@@ -395,7 +395,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ETokenType.DEC_OP, ENonTerminal.unary_expression],
[ENonTerminal.unary_operator, ENonTerminal.unary_expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.UnaryExpression.pool
// #endif
),
@@ -403,7 +403,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.unary_operator,
[[ETokenType.PLUS], [ETokenType.DASH], [ETokenType.BANG], [ETokenType.TILDE]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.UnaryOperator.pool
// #endif
),
@@ -468,7 +468,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ETokenType.XOR_ASSIGN],
[ETokenType.OR_ASSIGN]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.AssignmentOperator.pool
// #endif
),
@@ -566,7 +566,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.statement,
[[ENonTerminal.compound_statement], [ENonTerminal.simple_statement]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.Statement.pool
// #endif
),
@@ -586,7 +586,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ETokenType.LEFT_BRACE, ETokenType.RIGHT_BRACE],
[ENonTerminal.scope_brace, ENonTerminal.statement_list, ENonTerminal.scope_end_brace]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.CompoundStatement.pool
// #endif
),
@@ -600,7 +600,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.iteration_statement],
[ENonTerminal.jump_statement]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.SimpleStatement.pool
// #endif
),
@@ -673,7 +673,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.assignment_expression],
[ETokenType.LEFT_BRACE, ENonTerminal.initializer_list, ETokenType.RIGHT_BRACE]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.Initializer.pool
// #endif
),
@@ -681,7 +681,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.initializer_list,
[[ENonTerminal.initializer], [ENonTerminal.initializer_list, ETokenType.COMMA, ENonTerminal.initializer]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.InitializerList.pool
// #endif
),
@@ -689,7 +689,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.expression_statement,
[[ETokenType.SEMICOLON], [ENonTerminal.expression, ETokenType.SEMICOLON]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.ExpressionStatement.pool
// #endif
),
@@ -709,7 +709,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
ENonTerminal.statement
]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.SelectionStatement.pool
// #endif
),
@@ -727,7 +727,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
ENonTerminal.statement
]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.IterationStatement.pool
// #endif
),
@@ -748,7 +748,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.for_init_statement,
[[ENonTerminal.expression_statement], [ENonTerminal.declaration]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.ForInitStatement.pool
// #endif
),
@@ -759,7 +759,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.expression],
[ENonTerminal.fully_specified_type, ETokenType.ID, ETokenType.EQUAL, ENonTerminal.initializer]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.Condition.pool
// #endif
),
@@ -770,7 +770,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
[ENonTerminal.conditionopt, ETokenType.SEMICOLON],
[ENonTerminal.conditionopt, ETokenType.SEMICOLON, ENonTerminal.expression]
],
// #if _EDITOR
// #if _VERBOSE
ASTNode.ForRestStatement.pool
// #endif
),
@@ -778,7 +778,7 @@ const productionAndRules: [GrammarSymbol[], TranslationRule | undefined][] = [
...GrammarUtils.createProductionWithOptions(
ENonTerminal.conditionopt,
[[ETokenType.EPSILON], [ENonTerminal.condition]],
// #if _EDITOR
// #if _VERBOSE
ASTNode.ConditionOpt.pool
// #endif
),

View File

@@ -161,7 +161,7 @@ export class LALR1 {
if (terminal === EKeyword.ELSE && exist.action === EAction.Shift && action.action === EAction.Reduce) {
return;
} else {
// #if _EDITOR
// #if _VERBOSE
console.warn(
`conflict detect: <Terminal ${GrammarUtils.toString(terminal)}>`,
Utils.printAction(exist),

View File

@@ -59,13 +59,13 @@ export default class StateItem {
}
advance() {
// #if _EDITOR
// #if _VERBOSE
if (this.canReduce()) throw `Error: advance reduce-able parsing state item`;
// #endif
return new StateItem(this.production, this.position + 1, this.lookaheadSet);
}
// #if _EDITOR
// #if _VERBOSE
toString() {
const coreItem = this.production.derivation.map((item) => GrammarUtils.toString(item));
coreItem[this.position] = "." + (coreItem[this.position] ?? "");

View File

@@ -5,8 +5,7 @@ import { ENonTerminal, GrammarSymbol } from "../parser/GrammarSymbol";
import Production from "./Production";
import { ActionInfo, EAction } from "./types";
import { ShaderLab } from "../ShaderLab";
import { ShaderLabObjectPool } from "../ShaderLabObjectPool";
import { IPoolElement } from "@galacean/engine";
import { ClearableObjectPool, IPoolElement } from "@galacean/engine";
import { NodeChild } from "../parser/types";
export default class GrammarUtils {
@@ -25,8 +24,7 @@ export default class GrammarUtils {
goal: ENonTerminal,
options: GrammarSymbol[][],
/** the ast node */
// astType?: ASTNodeConstructor
astTypePool?: ShaderLabObjectPool<
astTypePool?: ClearableObjectPool<
{ set: (loc: ShaderRange, children: NodeChild[]) => void } & IPoolElement & TreeNode
>
) {
@@ -74,6 +72,7 @@ export default class GrammarUtils {
return a.action === b.action && a.target === b.target;
}
// #if _VERBOSE
static printAction(actionInfo: ActionInfo) {
return `<Action: ${EAction[actionInfo.action]} -> ${
actionInfo.action === EAction.Reduce ? Production.pool.get(actionInfo.target!) : `State ${actionInfo.target!}`
@@ -84,4 +83,5 @@ export default class GrammarUtils {
const deriv = production.derivation.map((gs) => GrammarUtils.toString(gs)).join("|");
return `${ENonTerminal[production.goal]} :=> ${deriv}`;
}
// #endif
}

View File

@@ -12,7 +12,7 @@ export class Lexer extends BaseScanner {
reset(source: string) {
this._source = source;
this._currentIndex = 0;
// #if _EDITOR
// #if _VERBOSE
this._line = this._column = 0;
// #endif
}
@@ -278,8 +278,7 @@ export class Lexer extends BaseScanner {
return this._scanStringConst();
default:
console.log("at position", start);
throw `Unexpected character ${this.getCurChar()}`;
this.throwError(this.getCurPosition(), `Unexpected character ${this.getCurChar()}`);
}
return token;
}
@@ -314,7 +313,7 @@ export class Lexer extends BaseScanner {
private _getPosition(offset /** offset from starting point */ = 0) {
return ShaderLab.createPosition(
this.current - offset,
// #if _EDITOR
// #if _VERBOSE
this._line,
this._column - offset
// #endif
@@ -385,7 +384,8 @@ export class Lexer extends BaseScanner {
buffer.push(this.getCurChar());
this.advance();
}
if (!LexerUtils.isNum(this.getCurChar())) throw "lexing error, invalid exponent suffix.";
if (!LexerUtils.isNum(this.getCurChar()))
this.throwError(this.getCurPosition(), "lexing error, invalid exponent suffix.");
while (LexerUtils.isNum(this.getCurChar())) {
buffer.push(this.getCurChar());
this.advance();

View File

@@ -1,4 +1,4 @@
// #if _EDITOR
// #if _VERBOSE
import { BuiltinFunction, BuiltinVariable, NonGenericGalaceanType } from "./builtin";
// #endif
import { CodeGenVisitor } from "../codeGen";
@@ -8,10 +8,10 @@ import { EKeyword, ETokenType, TokenType, ShaderRange, GalaceanDataType, TypeAny
import SematicAnalyzer from "./SemanticAnalyzer";
import { ShaderData } from "./ShaderInfo";
import { ESymbolType, FnSymbol, StructSymbol, VarSymbol } from "./symbolTable";
import { ParserUtils } from "../Utils";
import { ParserUtils } from "../ParserUtils";
import { IParamInfo, NodeChild, StructProp, SymbolType } from "./types";
import { ShaderLabObjectPool } from "../ShaderLabObjectPool";
import { IPoolElement } from "@galacean/engine";
import { ClearableObjectPool, IPoolElement } from "@galacean/engine";
import { ShaderLabUtils } from "../ShaderLabUtils";
export abstract class TreeNode implements IPoolElement {
/** The non-terminal in grammar. */
@@ -44,7 +44,7 @@ export abstract class TreeNode implements IPoolElement {
}
export namespace ASTNode {
export type ASTNodePool = ShaderLabObjectPool<
export type ASTNodePool = ClearableObjectPool<
{ set: (loc: ShaderRange, children: NodeChild[]) => void } & IPoolElement & TreeNode
>;
@@ -63,7 +63,7 @@ export namespace ASTNode {
}
export class TrivialNode extends TreeNode {
static pool = new ShaderLabObjectPool(TrivialNode);
static pool = ShaderLabUtils.createObjectPool(TrivialNode);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal._ignore);
@@ -71,7 +71,7 @@ export namespace ASTNode {
}
export class ScopeBrace extends TreeNode {
static pool = new ShaderLabObjectPool(ScopeBrace);
static pool = ShaderLabUtils.createObjectPool(ScopeBrace);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.scope_brace);
@@ -83,7 +83,7 @@ export namespace ASTNode {
}
export class ScopeEndBrace extends TreeNode {
static pool = new ShaderLabObjectPool(ScopeEndBrace);
static pool = ShaderLabUtils.createObjectPool(ScopeEndBrace);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.scope_end_brace);
@@ -95,13 +95,13 @@ export namespace ASTNode {
}
export class JumpStatement extends TreeNode {
static pool = new ShaderLabObjectPool(JumpStatement);
static pool = ShaderLabUtils.createObjectPool(JumpStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.jump_statement);
}
// #if _EDITOR
// #if _VERBOSE
override semanticAnalyze(sa: SematicAnalyzer): void {
if (ASTNode._unwrapToken(this.children![0]).type === EKeyword.RETURN) {
// TODO: check the equality of function return type declared and this type.
@@ -114,9 +114,9 @@ export namespace ASTNode {
}
}
// #if _EDITOR
// #if _VERBOSE
export class ConditionOpt extends TreeNode {
static pool = new ShaderLabObjectPool(ConditionOpt);
static pool = ShaderLabUtils.createObjectPool(ConditionOpt);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.conditionopt);
@@ -124,7 +124,7 @@ export namespace ASTNode {
}
export class ForRestStatement extends TreeNode {
static pool = new ShaderLabObjectPool(ForRestStatement);
static pool = ShaderLabUtils.createObjectPool(ForRestStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.for_rest_statement);
@@ -132,7 +132,7 @@ export namespace ASTNode {
}
export class Condition extends TreeNode {
static pool = new ShaderLabObjectPool(Condition);
static pool = ShaderLabUtils.createObjectPool(Condition);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.condition);
@@ -140,7 +140,7 @@ export namespace ASTNode {
}
export class ForInitStatement extends TreeNode {
static pool = new ShaderLabObjectPool(ForInitStatement);
static pool = ShaderLabUtils.createObjectPool(ForInitStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.for_init_statement);
@@ -148,7 +148,7 @@ export namespace ASTNode {
}
export class IterationStatement extends TreeNode {
static pool = new ShaderLabObjectPool(IterationStatement);
static pool = ShaderLabUtils.createObjectPool(IterationStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.iteration_statement);
@@ -156,7 +156,7 @@ export namespace ASTNode {
}
export class SelectionStatement extends TreeNode {
static pool = new ShaderLabObjectPool(SelectionStatement);
static pool = ShaderLabUtils.createObjectPool(SelectionStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.selection_statement);
@@ -164,7 +164,7 @@ export namespace ASTNode {
}
export class ExpressionStatement extends TreeNode {
static pool = new ShaderLabObjectPool(ExpressionStatement);
static pool = ShaderLabUtils.createObjectPool(ExpressionStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.expression_statement);
@@ -187,9 +187,9 @@ export namespace ASTNode {
}
}
// #if _EDITOR
// #if _VERBOSE
export class InitializerList extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(InitializerList);
static pool = ShaderLabUtils.createObjectPool(InitializerList);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.initializer_list);
@@ -202,7 +202,7 @@ export namespace ASTNode {
}
export class Initializer extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(Initializer);
static pool = ShaderLabUtils.createObjectPool(Initializer);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.initializer);
@@ -219,7 +219,7 @@ export namespace ASTNode {
// #endif
export class SingleDeclaration extends TreeNode {
static pool = new ShaderLabObjectPool(SingleDeclaration);
static pool = ShaderLabUtils.createObjectPool(SingleDeclaration);
typeSpecifier: TypeSpecifier;
arraySpecifier?: ArraySpecifier;
@@ -258,7 +258,7 @@ export namespace ASTNode {
}
export class FullySpecifiedType extends TreeNode {
static pool = new ShaderLabObjectPool(FullySpecifiedType);
static pool = ShaderLabUtils.createObjectPool(FullySpecifiedType);
get qualifierList() {
if (this.children.length > 1) {
@@ -280,7 +280,7 @@ export namespace ASTNode {
}
export class TypeQualifier extends TreeNode {
static pool = new ShaderLabObjectPool(TypeQualifier);
static pool = ShaderLabUtils.createObjectPool(TypeQualifier);
qualifierList: EKeyword[];
@@ -301,7 +301,7 @@ export namespace ASTNode {
}
export class SingleTypeQualifier extends TreeNode {
static pool = new ShaderLabObjectPool(SingleTypeQualifier);
static pool = ShaderLabUtils.createObjectPool(SingleTypeQualifier);
qualifier: EKeyword;
lexeme: string;
@@ -335,9 +335,9 @@ export namespace ASTNode {
}
}
// #if _EDITOR
// #if _VERBOSE
export class StorageQualifier extends BasicTypeQualifier {
static pool = new ShaderLabObjectPool(StorageQualifier);
static pool = ShaderLabUtils.createObjectPool(StorageQualifier);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.storage_qualifier);
@@ -345,7 +345,7 @@ export namespace ASTNode {
}
export class PrecisionQualifier extends BasicTypeQualifier {
static pool = new ShaderLabObjectPool(PrecisionQualifier);
static pool = ShaderLabUtils.createObjectPool(PrecisionQualifier);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.precision_qualifier);
@@ -353,7 +353,7 @@ export namespace ASTNode {
}
export class InterpolationQualifier extends BasicTypeQualifier {
static pool = new ShaderLabObjectPool(InterpolationQualifier);
static pool = ShaderLabUtils.createObjectPool(InterpolationQualifier);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.interpolation_qualifier);
@@ -361,7 +361,7 @@ export namespace ASTNode {
}
export class InvariantQualifier extends BasicTypeQualifier {
static pool = new ShaderLabObjectPool(InvariantQualifier);
static pool = ShaderLabUtils.createObjectPool(InvariantQualifier);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.invariant_qualifier);
@@ -370,7 +370,7 @@ export namespace ASTNode {
// #endif
export class TypeSpecifier extends TreeNode {
static pool = new ShaderLabObjectPool(TypeSpecifier);
static pool = ShaderLabUtils.createObjectPool(TypeSpecifier);
get type(): GalaceanDataType {
return (this.children![0] as TypeSpecifierNonArray).type;
@@ -392,7 +392,7 @@ export namespace ASTNode {
}
export class ArraySpecifier extends TreeNode {
static pool = new ShaderLabObjectPool(ArraySpecifier);
static pool = ShaderLabUtils.createObjectPool(ArraySpecifier);
get size(): number | undefined {
const integerConstantExpr = this.children[1] as IntegerConstantExpression;
@@ -405,7 +405,7 @@ export namespace ASTNode {
}
export class IntegerConstantExpressionOperator extends TreeNode {
static pool = new ShaderLabObjectPool(IntegerConstantExpressionOperator);
static pool = ShaderLabUtils.createObjectPool(IntegerConstantExpressionOperator);
compute: (a: number, b: number) => number;
get lexeme(): string {
@@ -435,13 +435,13 @@ export namespace ASTNode {
this.compute = (a, b) => a % b;
break;
default:
throw `not implemented operator ${operator.lexeme}`;
sa.error(operator.location, `not implemented operator ${operator.lexeme}`);
}
}
}
export class IntegerConstantExpression extends TreeNode {
static pool = new ShaderLabObjectPool(IntegerConstantExpression);
static pool = ShaderLabUtils.createObjectPool(IntegerConstantExpression);
value?: number;
override set(loc: ShaderRange, children: NodeChild[]) {
@@ -455,14 +455,14 @@ export namespace ASTNode {
if (child instanceof Token) {
this.value = Number(child.lexeme);
}
// #if _EDITOR
// #if _VERBOSE
else {
const id = child as VariableIdentifier;
if (!id.symbolInfo) {
sa.error(id.location, "undeclared symbol:", id.lexeme);
sa.error(id.location, "Undeclared symbol:", id.lexeme);
}
if (!ParserUtils.typeCompatible(EKeyword.INT, id.typeInfo)) {
sa.error(id.location, "invalid integer.");
sa.error(id.location, "Invalid integer.");
return;
}
}
@@ -472,7 +472,7 @@ export namespace ASTNode {
}
export class TypeSpecifierNonArray extends TreeNode {
static pool = new ShaderLabObjectPool(TypeSpecifierNonArray);
static pool = ShaderLabUtils.createObjectPool(TypeSpecifierNonArray);
type: GalaceanDataType;
lexeme: string;
@@ -490,7 +490,7 @@ export namespace ASTNode {
}
export class ExtBuiltinTypeSpecifierNonArray extends TreeNode {
static pool = new ShaderLabObjectPool(ExtBuiltinTypeSpecifierNonArray);
static pool = ShaderLabUtils.createObjectPool(ExtBuiltinTypeSpecifierNonArray);
type: TokenType;
lexeme: string;
@@ -504,7 +504,7 @@ export namespace ASTNode {
}
export class InitDeclaratorList extends TreeNode {
static pool = new ShaderLabObjectPool(InitDeclaratorList);
static pool = ShaderLabUtils.createObjectPool(InitDeclaratorList);
get typeInfo(): SymbolType {
if (this.children.length === 1) {
@@ -533,9 +533,9 @@ export namespace ASTNode {
} else if (this.children.length === 4 || this.children.length === 6) {
const typeInfo = this.typeInfo;
const arraySpecifier = this.children[3] as ArraySpecifier;
// #if _EDITOR
// #if _VERBOSE
if (typeInfo.arraySpecifier && arraySpecifier) {
sa.error(arraySpecifier.location, "array of array is not supported.");
sa.error(arraySpecifier.location, "Array of array is not supported.");
}
// #endif
typeInfo.arraySpecifier = arraySpecifier;
@@ -547,7 +547,7 @@ export namespace ASTNode {
}
export class IdentifierList extends TreeNode {
static pool = new ShaderLabObjectPool(IdentifierList);
static pool = ShaderLabUtils.createObjectPool(IdentifierList);
get idList(): Token[] {
if (this.children.length === 2) {
@@ -562,7 +562,7 @@ export namespace ASTNode {
}
export class Declaration extends TreeNode {
static pool = new ShaderLabObjectPool(Declaration);
static pool = ShaderLabUtils.createObjectPool(Declaration);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.declaration);
@@ -574,7 +574,7 @@ export namespace ASTNode {
}
export class FunctionProtoType extends TreeNode {
static pool = new ShaderLabObjectPool(FunctionProtoType);
static pool = ShaderLabUtils.createObjectPool(FunctionProtoType);
private get declarator() {
return this.children[0] as FunctionDeclarator;
@@ -606,7 +606,7 @@ export namespace ASTNode {
}
export class FunctionDeclarator extends TreeNode {
static pool = new ShaderLabObjectPool(FunctionDeclarator);
static pool = ShaderLabUtils.createObjectPool(FunctionDeclarator);
private get header() {
return this.children[0] as FunctionHeader;
@@ -638,7 +638,7 @@ export namespace ASTNode {
}
export class FunctionHeader extends TreeNode {
static pool = new ShaderLabObjectPool(FunctionHeader);
static pool = ShaderLabUtils.createObjectPool(FunctionHeader);
get ident() {
return this.children[1] as Token;
@@ -661,7 +661,7 @@ export namespace ASTNode {
}
export class FunctionParameterList extends TreeNode {
static pool = new ShaderLabObjectPool(FunctionParameterList);
static pool = ShaderLabUtils.createObjectPool(FunctionParameterList);
get parameterInfoList(): IParamInfo[] {
if (this.children.length === 1) {
@@ -694,7 +694,7 @@ export namespace ASTNode {
}
export class ParameterDeclaration extends TreeNode {
static pool = new ShaderLabObjectPool(ParameterDeclaration);
static pool = ShaderLabUtils.createObjectPool(ParameterDeclaration);
get typeQualifier() {
if (this.children.length === 2) return this.children[0] as TypeQualifier;
@@ -730,7 +730,7 @@ export namespace ASTNode {
}
export class ParameterDeclarator extends TreeNode {
static pool = new ShaderLabObjectPool(ParameterDeclarator);
static pool = ShaderLabUtils.createObjectPool(ParameterDeclarator);
get ident() {
return this.children[1] as Token;
@@ -747,9 +747,9 @@ export namespace ASTNode {
}
}
// #if _EDITOR
// #if _VERBOSE
export class SimpleStatement extends TreeNode {
static pool = new ShaderLabObjectPool(SimpleStatement);
static pool = ShaderLabUtils.createObjectPool(SimpleStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.simple_statement);
@@ -757,7 +757,7 @@ export namespace ASTNode {
}
export class CompoundStatement extends TreeNode {
static pool = new ShaderLabObjectPool(CompoundStatement);
static pool = ShaderLabUtils.createObjectPool(CompoundStatement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.compound_statement);
@@ -766,16 +766,16 @@ export namespace ASTNode {
// #endif
export class CompoundStatementNoScope extends TreeNode {
static pool = new ShaderLabObjectPool(CompoundStatementNoScope);
static pool = ShaderLabUtils.createObjectPool(CompoundStatementNoScope);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.compound_statement_no_scope);
}
}
// #if _EDITOR
// #if _VERBOSE
export class Statement extends TreeNode {
static pool = new ShaderLabObjectPool(Statement);
static pool = ShaderLabUtils.createObjectPool(Statement);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.statement);
@@ -784,7 +784,7 @@ export namespace ASTNode {
// #endif
export class StatementList extends TreeNode {
static pool = new ShaderLabObjectPool(StatementList);
static pool = ShaderLabUtils.createObjectPool(StatementList);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.statement_list);
@@ -796,7 +796,7 @@ export namespace ASTNode {
}
export class FunctionDefinition extends TreeNode {
static pool = new ShaderLabObjectPool(FunctionDefinition);
static pool = ShaderLabUtils.createObjectPool(FunctionDefinition);
get protoType() {
return this.children[0] as FunctionProtoType;
@@ -822,7 +822,7 @@ export namespace ASTNode {
}
export class FunctionCall extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(FunctionCall);
static pool = ShaderLabUtils.createObjectPool(FunctionCall);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.function_call);
@@ -838,7 +838,7 @@ export namespace ASTNode {
}
export class FunctionCallGeneric extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(FunctionCallGeneric);
static pool = ShaderLabUtils.createObjectPool(FunctionCallGeneric);
fnSymbol: FnSymbol | StructSymbol | undefined;
@@ -861,7 +861,7 @@ export namespace ASTNode {
paramSig = paramList.paramSig as any;
}
}
// #if _EDITOR
// #if _VERBOSE
const builtinFn = BuiltinFunction.getFn(fnIdent, ...(paramSig ?? []));
if (builtinFn) {
this.type = BuiltinFunction.getReturnType(builtinFn.fun, builtinFn.genType);
@@ -871,8 +871,8 @@ export namespace ASTNode {
const fnSymbol = sa.symbolTable.lookup({ ident: fnIdent, symbolType: ESymbolType.FN, signature: paramSig });
if (!fnSymbol) {
// #if _EDITOR
sa.error(this.location, "no overload function type found:", functionIdentifier.ident);
// #if _VERBOSE
sa.error(this.location, "No overload function type found: ", functionIdentifier.ident);
// #endif
return;
}
@@ -883,7 +883,7 @@ export namespace ASTNode {
}
export class FunctionCallParameterList extends TreeNode {
static pool = new ShaderLabObjectPool(FunctionCallParameterList);
static pool = ShaderLabUtils.createObjectPool(FunctionCallParameterList);
get paramSig(): GalaceanDataType[] | undefined {
if (this.children.length === 1) {
@@ -918,7 +918,7 @@ export namespace ASTNode {
}
export class PrecisionSpecifier extends TreeNode {
static pool = new ShaderLabObjectPool(PrecisionSpecifier);
static pool = ShaderLabUtils.createObjectPool(PrecisionSpecifier);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.precision_specifier);
@@ -930,7 +930,7 @@ export namespace ASTNode {
}
export class FunctionIdentifier extends TreeNode {
static pool = new ShaderLabObjectPool(FunctionIdentifier);
static pool = ShaderLabUtils.createObjectPool(FunctionIdentifier);
get ident() {
const ty = this.children[0] as TypeSpecifier;
@@ -958,13 +958,13 @@ export namespace ASTNode {
}
export class AssignmentExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(AssignmentExpression);
static pool = ShaderLabUtils.createObjectPool(AssignmentExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.assignment_expression);
}
// #if _EDITOR
// #if _VERBOSE
override semanticAnalyze(sa: SematicAnalyzer): void {
if (this.children.length === 1) {
const expr = this.children[0] as ConditionalExpression;
@@ -977,9 +977,9 @@ export namespace ASTNode {
// #endif
}
// #if _EDITOR
// #if _VERBOSE
export class AssignmentOperator extends TreeNode {
static pool = new ShaderLabObjectPool(AssignmentOperator);
static pool = ShaderLabUtils.createObjectPool(AssignmentOperator);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.assignment_operator);
@@ -988,13 +988,13 @@ export namespace ASTNode {
// #endif
export class Expression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(Expression);
static pool = ShaderLabUtils.createObjectPool(Expression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.expression);
}
// #if _EDITOR
// #if _VERBOSE
override semanticAnalyze(sa: SematicAnalyzer): void {
if (this.children.length === 1) {
const expr = this.children[0] as AssignmentExpression;
@@ -1008,7 +1008,7 @@ export namespace ASTNode {
}
export class PrimaryExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(PrimaryExpression);
static pool = ShaderLabUtils.createObjectPool(PrimaryExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.primary_expression);
@@ -1041,7 +1041,7 @@ export namespace ASTNode {
}
export class PostfixExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(PostfixExpression);
static pool = ShaderLabUtils.createObjectPool(PostfixExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.postfix_expression);
@@ -1056,9 +1056,9 @@ export namespace ASTNode {
}
}
// #if _EDITOR
// #if _VERBOSE
export class UnaryOperator extends TreeNode {
static pool = new ShaderLabObjectPool(UnaryOperator);
static pool = ShaderLabUtils.createObjectPool(UnaryOperator);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.unary_operator);
@@ -1066,9 +1066,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class UnaryExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(UnaryExpression);
static pool = ShaderLabUtils.createObjectPool(UnaryExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.unary_expression);
@@ -1077,9 +1077,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class MultiplicativeExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(MultiplicativeExpression);
static pool = ShaderLabUtils.createObjectPool(MultiplicativeExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.multiplicative_expression);
@@ -1096,9 +1096,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class AdditiveExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(AdditiveExpression);
static pool = ShaderLabUtils.createObjectPool(AdditiveExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.additive_expression);
@@ -1115,9 +1115,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class ShiftExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(ShiftExpression);
static pool = ShaderLabUtils.createObjectPool(ShiftExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.shift_expression);
@@ -1130,9 +1130,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class RelationalExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(RelationalExpression);
static pool = ShaderLabUtils.createObjectPool(RelationalExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.relational_expression);
@@ -1148,9 +1148,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class EqualityExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(EqualityExpression);
static pool = ShaderLabUtils.createObjectPool(EqualityExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.equality_expression);
@@ -1166,9 +1166,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class AndExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(AndExpression);
static pool = ShaderLabUtils.createObjectPool(AndExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.and_expression);
@@ -1184,9 +1184,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class ExclusiveOrExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(ExclusiveOrExpression);
static pool = ShaderLabUtils.createObjectPool(ExclusiveOrExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.exclusive_or_expression);
@@ -1202,9 +1202,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class InclusiveOrExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(InclusiveOrExpression);
static pool = ShaderLabUtils.createObjectPool(InclusiveOrExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.inclusive_or_expression);
@@ -1220,9 +1220,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class LogicalAndExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(LogicalAndExpression);
static pool = ShaderLabUtils.createObjectPool(LogicalAndExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.logical_and_expression);
@@ -1238,9 +1238,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class LogicalXorExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(LogicalXorExpression);
static pool = ShaderLabUtils.createObjectPool(LogicalXorExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.logical_xor_expression);
@@ -1256,9 +1256,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class LogicalOrExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(LogicalOrExpression);
static pool = ShaderLabUtils.createObjectPool(LogicalOrExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.logical_or_expression);
@@ -1274,9 +1274,9 @@ export namespace ASTNode {
}
// #endif
// #if _EDITOR
// #if _VERBOSE
export class ConditionalExpression extends ExpressionAstNode {
static pool = new ShaderLabObjectPool(ConditionalExpression);
static pool = ShaderLabUtils.createObjectPool(ConditionalExpression);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.conditional_expression);
@@ -1291,7 +1291,7 @@ export namespace ASTNode {
// #endif
export class StructSpecifier extends TreeNode {
static pool = new ShaderLabObjectPool(StructSpecifier);
static pool = ShaderLabUtils.createObjectPool(StructSpecifier);
ident?: Token;
@@ -1313,7 +1313,7 @@ export namespace ASTNode {
}
export class StructDeclarationList extends TreeNode {
static pool = new ShaderLabObjectPool(StructDeclarationList);
static pool = ShaderLabUtils.createObjectPool(StructDeclarationList);
get propList(): StructProp[] {
if (this.children.length === 1) {
@@ -1330,7 +1330,7 @@ export namespace ASTNode {
}
export class StructDeclaration extends TreeNode {
static pool = new ShaderLabObjectPool(StructDeclaration);
static pool = ShaderLabUtils.createObjectPool(StructDeclaration);
get typeSpecifier() {
if (this.children.length === 3) {
@@ -1363,7 +1363,7 @@ export namespace ASTNode {
}
export class StructDeclaratorList extends TreeNode {
static pool = new ShaderLabObjectPool(StructDeclaratorList);
static pool = ShaderLabUtils.createObjectPool(StructDeclaratorList);
get declaratorList(): StructDeclarator[] {
if (this.children.length === 1) {
@@ -1380,7 +1380,7 @@ export namespace ASTNode {
}
export class StructDeclarator extends TreeNode {
static pool = new ShaderLabObjectPool(StructDeclarator);
static pool = ShaderLabUtils.createObjectPool(StructDeclarator);
get ident() {
return this.children[0] as Token;
@@ -1396,7 +1396,7 @@ export namespace ASTNode {
}
export class VariableDeclaration extends TreeNode {
static pool = new ShaderLabObjectPool(VariableDeclaration);
static pool = ShaderLabUtils.createObjectPool(VariableDeclaration);
override set(loc: ShaderRange, children: NodeChild[]) {
super.set(loc, children, ENonTerminal.variable_declaration);
@@ -1417,11 +1417,11 @@ export namespace ASTNode {
}
export class VariableIdentifier extends TreeNode {
static pool = new ShaderLabObjectPool(VariableIdentifier);
static pool = ShaderLabUtils.createObjectPool(VariableIdentifier);
symbolInfo:
| VarSymbol
// #if _EDITOR
// #if _VERBOSE
| BuiltinVariable
// #endif
| null;
@@ -1442,7 +1442,7 @@ export namespace ASTNode {
override semanticAnalyze(sa: SematicAnalyzer): void {
const token = this.children[0] as Token;
// #if _EDITOR
// #if _VERBOSE
const builtinVar = BuiltinVariable.getVar(token.lexeme);
if (builtinVar) {
this.symbolInfo = builtinVar;
@@ -1451,7 +1451,7 @@ export namespace ASTNode {
// #endif
this.symbolInfo = sa.symbolTable.lookup({ ident: token.lexeme, symbolType: ESymbolType.VAR }) as VarSymbol;
// #if _EDITOR
// #if _VERBOSE
if (!this.symbolInfo) {
sa.error(this.location, "undeclared identifier:", token.lexeme);
}
@@ -1464,7 +1464,7 @@ export namespace ASTNode {
}
export class GLShaderProgram extends TreeNode {
static pool = new ShaderLabObjectPool(GLShaderProgram);
static pool = ShaderLabUtils.createObjectPool(GLShaderProgram);
shaderData: ShaderData;

View File

@@ -1,13 +1,14 @@
import { ShaderRange } from "../common";
import { TreeNode } from "./AST";
// #if _EDITOR
import { SemanticError } from "../Error";
// #endif
import { GSErrorName } from "../GSError";
import { ShaderData } from "./ShaderInfo";
import { SymbolInfo, SymbolTable } from "../parser/symbolTable";
import { NodeChild } from "./types";
import { SymbolTableStack } from "../common/BaseSymbolTable";
import { Logger } from "@galacean/engine";
import { ShaderLab } from "../ShaderLab";
// #if _VERBOSE
import { GSError } from "../GSError";
// #endif
export type TranslationRule<T = any> = (sa: SematicAnalyzer, ...tokens: NodeChild[]) => T;
@@ -22,8 +23,8 @@ export default class SematicAnalyzer {
symbolTable: SymbolTableStack<SymbolInfo, SymbolTable> = new SymbolTableStack();
private _shaderData = new ShaderData();
// #if _EDITOR
readonly errors: SemanticError[] = [];
// #if _VERBOSE
readonly errors: GSError[] = [];
// #endif
get shaderData() {
@@ -41,7 +42,7 @@ export default class SematicAnalyzer {
this._shaderData = new ShaderData();
this.symbolTable.clear();
this.newScope();
// #if _EDITOR
// #if _VERBOSE
this.errors.length = 0;
// #endif
}
@@ -63,13 +64,13 @@ export default class SematicAnalyzer {
return this._translationRuleTable.get(pid);
}
// #if _EDITOR
error(loc: ShaderRange, ...param: any[]) {
Logger.error(loc, ...param);
const err = new SemanticError(param.join(""), loc);
// #if _VERBOSE
const err = new GSError(GSErrorName.CompilationError, param.join(""), loc, ShaderLab._processingPassText);
this.errors.push(err);
return err;
// #else
throw new Error(param.join(""));
// #endif
}
// #endif
}

View File

@@ -8,8 +8,11 @@ import SematicAnalyzer from "./SemanticAnalyzer";
import { TraceStackItem } from "./types";
import { addTranslationRule, createGrammar } from "../lalr/CFG";
import { LALR1 } from "../lalr";
import { ParserUtils } from "../Utils";
import { ParserUtils } from "../ParserUtils";
import { Logger } from "@galacean/engine";
import { GSErrorName } from "../GSError";
import { ShaderLab } from "../ShaderLab";
import { ShaderLabUtils } from "../ShaderLabUtils";
/**
* The syntax parser and sematic analyzer of `ShaderLab` compiler
@@ -31,6 +34,13 @@ export class ShaderTargetParser {
return this.gotoTable.get(this.curState);
}
// #if _VERBOSE
/** @internal */
get errors() {
return this.sematicAnalyzer.errors;
}
// #endif
static _singleton: ShaderTargetParser;
static create() {
@@ -103,13 +113,21 @@ export class ShaderTargetParser {
traceBackStack.push(nextState);
continue;
} else {
Logger.error(token.location, `parse error token ${token.lexeme}`);
const error = ShaderLabUtils.createGSError(
`Unexpected token ${token.lexeme}`,
GSErrorName.CompilationError,
ShaderLab._processingPassText,
token.location
);
// #if _VERBOSE
this.sematicAnalyzer.errors.push(error);
return null;
// #endif
}
}
}
// #if _EDITOR
// #if _VERBOSE
private _printStack(nextToken: BaseToken) {
let str = "";
for (let i = 0; i < this._traceBackStack.length - 1; i++) {

View File

@@ -126,18 +126,16 @@ BuiltinFunction._create("ceil", EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("fract", EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("mod", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT);
BuiltinFunction._create("mod", EGenType.GenType, EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("min", EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("min", EGenType.GenType, EKeyword.FLOAT);
BuiltinFunction._create("min", EGenType.GenIntType, EGenType.GenIntType);
BuiltinFunction._create("min", EGenType.GenIntType, EKeyword.INT);
BuiltinFunction._create("min", EGenType.GenType, EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("min", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT);
BuiltinFunction._create("min", EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType);
BuiltinFunction._create("min", EGenType.GenIntType, EGenType.GenIntType, EKeyword.INT);
BuiltinFunction._create("min", EGenType.GenUintType, EGenType.GenUintType, EGenType.GenUintType);
BuiltinFunction._create("min", EGenType.GenUintType, EGenType.GenUintType, EKeyword.UINT);
BuiltinFunction._create("max", EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("max", EGenType.GenType, EKeyword.FLOAT);
BuiltinFunction._create("max", EGenType.GenIntType, EGenType.GenIntType);
BuiltinFunction._create("max", EGenType.GenIntType, EKeyword.INT);
BuiltinFunction._create("max", EGenType.GenUintType, EGenType.GenUintType, EGenType.GenUintType);
BuiltinFunction._create("max", EGenType.GenUintType, EGenType.GenUintType, EKeyword.UINT);
BuiltinFunction._create("max", EGenType.GenType, EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("max", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT);
BuiltinFunction._create("max", EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType);
BuiltinFunction._create("max", EGenType.GenIntType, EGenType.GenIntType, EKeyword.INT);
BuiltinFunction._create("clamp", EGenType.GenType, EGenType.GenType, EGenType.GenType, EGenType.GenType);
BuiltinFunction._create("clamp", EGenType.GenType, EGenType.GenType, EKeyword.FLOAT, EKeyword.FLOAT);
BuiltinFunction._create("clamp", EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType, EGenType.GenIntType);

View File

@@ -1,4 +1,4 @@
// #if _EDITOR
// #if _VERBOSE
export * from "./functions";
export * from "./variables";
// #endif

View File

@@ -1,49 +1,37 @@
import { BaseToken } from "../common/BaseToken";
import { ShaderRange } from "../common";
import { ParserUtils } from "../Utils";
import { ShaderLabUtils } from "../ShaderLabUtils";
// #if _VERBOSE
import { GSErrorName } from "../GSError";
// #endif
export class MacroDefine {
readonly location?: ShaderRange;
readonly macro: BaseToken;
readonly args?: BaseToken[];
readonly body?: BaseToken;
private _replaceRegex?: RegExp;
private readonly _argsLexemes: string[];
get isFunction() {
get isFunction(): boolean {
return !!this.args?.length;
}
get macroLexeme() {
return this.macro.lexeme;
}
constructor(macro: BaseToken, body?: BaseToken, loc?: ShaderRange, args?: BaseToken[]) {
this.location = loc;
this.macro = macro;
this.body = body;
this.args = args;
}
private _expand(...args: string[]): string {
if (this.isFunction) {
const argsTextList = this.args!.map((item) => item.lexeme);
// #if _EDITOR
if (args.length !== this.args?.length) {
ParserUtils.throw(this.location, "mismatched function macro");
}
// #endif
const replaceRegex = new RegExp(`\\b(${argsTextList.join("|")})\\b`, "g");
return this.body.lexeme.replaceAll(replaceRegex, (_, m) => {
const idx = argsTextList.findIndex((item) => item === m);
return args[idx];
});
constructor(
public readonly macro: BaseToken,
public readonly body?: BaseToken,
public readonly location?: ShaderRange,
public readonly args?: BaseToken[]
) {
if (args) {
this._argsLexemes = this.args.map((item) => item.lexeme);
this._replaceRegex = new RegExp(`\\b(${this._argsLexemes.join("|")})\\b`, "g");
}
return this.body.lexeme;
}
expand(...args: string[]): string {
const ret = this._expand(...args);
// TODO: erase the comments, any more performant and lightweight solution?
return ret.replaceAll(/(\/\/[^\n]*|\/\*.*\*\/)/gs, "");
expandFunctionBody(args: string[]): string {
if (args.length !== this.args?.length) {
throw ShaderLabUtils.createGSError("mismatched function macro", GSErrorName.PreprocessorError, "", this.location);
}
return this.body.lexeme.replace(this._replaceRegex, (m) => {
return args[this._argsLexemes.indexOf(m)];
});
}
}

View File

@@ -1,19 +1,20 @@
import { ShaderRange } from "../common";
import { ShaderPosition, ShaderRange } from "../common";
import LexerUtils from "../lexer/Utils";
import { MacroDefine } from "./MacroDefine";
// #if _EDITOR
import PpSourceMap, { BlockInfo } from "./sourceMap";
// #endif
import { BaseToken } from "../common/BaseToken";
import { ParserUtils } from "../Utils";
import { EPpKeyword, EPpToken, PpConstant } from "./constants";
import PpScanner from "./PpScanner";
import { PpUtils } from "./Utils";
import { ShaderLab } from "../ShaderLab";
import { ShaderPass } from "@galacean/engine";
import { ShaderLabUtils } from "../ShaderLabUtils";
import { GSErrorName } from "../GSError";
// #if _VERBOSE
import PpSourceMap, { BlockInfo } from "./sourceMap";
// #endif
export interface ExpandSegment {
// #if _EDITOR
// #if _VERBOSE
block?: BlockInfo;
// #endif
rangeInBlock: ShaderRange;
@@ -21,7 +22,7 @@ export interface ExpandSegment {
}
/** @internal */
export default class PpParser {
export class PpParser {
private static _definedMacros: Map<string, MacroDefine> = new Map();
private static _expandSegmentsStack: ExpandSegment[][] = [[]];
@@ -31,6 +32,10 @@ export default class PpParser {
private static _includeMap: Record<string, string>;
private static _basePathForIncludeKey: string;
// #if _VERBOSE
static _errors: Error[] = [];
// #endif
static reset(includeMap: Record<string, string>, basePathForIncludeKey: string) {
this._definedMacros.clear();
this._expandSegmentsStack.length = 0;
@@ -39,6 +44,9 @@ export default class PpParser {
this.addPredefinedMacro("GL_ES");
this._includeMap = includeMap;
this._basePathForIncludeKey = basePathForIncludeKey;
// #if _VERBOSE
this._errors.length = 0;
// #endif
}
static addPredefinedMacro(macro: string, value?: string) {
@@ -54,7 +62,7 @@ export default class PpParser {
this._definedMacros.set(macro, new MacroDefine(tk, macroBody));
}
static parse(scanner: PpScanner): string {
static parse(scanner: PpScanner): string | null {
while (!scanner.isEnd()) {
const directive = scanner.scanDirective(this._onToken.bind(this))!;
if (scanner.isEnd()) break;
@@ -84,6 +92,9 @@ export default class PpParser {
break;
}
}
// #if _VERBOSE
if (this._errors.length > 0) return null;
// #endif
return PpUtils.expand(this.expandSegments, scanner.source, scanner.sourceMap);
}
@@ -92,6 +103,11 @@ export default class PpParser {
return this._expandSegmentsStack[this._expandSegmentsStack.length - 1];
}
private static reportError(loc: ShaderRange | ShaderPosition, message: string, source: string, file: string) {
const error = ShaderLabUtils.createGSError(message, GSErrorName.PreprocessorError, source, loc, file);
this._errors.push(error);
}
private static _parseInclude(scanner: PpScanner) {
const start = scanner.getShaderPosition(8);
@@ -111,16 +127,17 @@ export default class PpParser {
const end = scanner.getShaderPosition();
const chunk = this._includeMap[includedPath];
if (!chunk) {
ParserUtils.throw(id.location, `Shader slice "${includedPath}" not founded.`);
this.reportError(id.location, `Shader slice "${includedPath}" not founded.`, scanner.source, scanner.file);
return;
}
const range = ShaderLab.createRange(start, end);
const expanded = this._expandMacroChunk(chunk, range, id.lexeme);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(id.lexeme, undefined, expanded.sourceMap);
// #endif
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
@@ -144,13 +161,13 @@ export default class PpParser {
const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap);
// #endif
const range = ShaderLab.createRange(bodyChunk.location.start, end);
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
@@ -179,13 +196,13 @@ export default class PpParser {
if (directive === EPpKeyword.else) {
const { token: elseChunk } = scanner.scanMacroBranchChunk();
const expanded = this._expandMacroChunk(elseChunk.lexeme, elseChunk.location, scanner);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap);
// #endif
const startPosition = ShaderLab.createPosition(start);
const range = ShaderLab.createRange(startPosition, scanner.getShaderPosition());
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
@@ -197,28 +214,28 @@ export default class PpParser {
if (!!constantExpr) {
const end = nextDirective.type === EPpKeyword.endif ? scanner.current : scanner.scanRemainMacro().index;
const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap);
// #endif
const startPosition = ShaderLab.createPosition(start);
const endPosition = ShaderLab.createPosition(end);
const range = ShaderLab.createRange(startPosition, endPosition);
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
replace: expanded.content
});
} else {
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange);
// #endif
const startPosition = ShaderLab.createPosition(start);
const endPosition = ShaderLab.createPosition(scanner.current);
const range = ShaderLab.createRange(startPosition, endPosition);
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
@@ -285,7 +302,8 @@ export default class PpParser {
scanner.skipSpace(false);
const operand2 = this._parseRelationalExpression(scanner) as number;
if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") {
ParserUtils.throw(opPos, "invalid operator in relation expression.");
this.reportError(opPos, "invalid operator in relation expression.", scanner.source, scanner.file);
return;
}
switch (operator) {
case ">":
@@ -310,7 +328,8 @@ export default class PpParser {
scanner.skipSpace(false);
const operand2 = this._parseShiftExpression(scanner) as number;
if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") {
ParserUtils.throw(opPos, "invalid operator in shift expression.");
this.reportError(opPos, "invalid operator in shift expression.", scanner.source, scanner.file);
return;
}
switch (operator) {
case ">>":
@@ -333,7 +352,8 @@ export default class PpParser {
scanner.skipSpace(false);
const operand2 = this._parseAdditiveExpression(scanner) as number;
if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") {
ParserUtils.throw(opPos, "invalid operator.");
this.reportError(opPos, "invalid operator.", scanner.source, scanner.file);
return false;
}
switch (operator) {
case "+":
@@ -354,7 +374,8 @@ export default class PpParser {
scanner.skipSpace(false);
const operand2 = this._parseMulticativeExpression(scanner) as number;
if (typeof operand1 !== typeof operand2 && typeof operand1 !== "number") {
ParserUtils.throw(opPos, "invalid operator.");
this.reportError(opPos, "invalid operator.", scanner.source, scanner.file);
return;
}
switch (operator) {
case "*":
@@ -375,7 +396,7 @@ export default class PpParser {
const opPos = scanner.getShaderPosition();
const parenExpr = this._parseParenthesisExpression(scanner);
if ((operator === "!" && typeof parenExpr !== "boolean") || (operator !== "!" && typeof parenExpr !== "number")) {
ParserUtils.throw(opPos, "invalid operator.");
this.reportError(opPos, "invalid operator.", scanner.source, scanner.file);
}
switch (operator) {
@@ -417,15 +438,14 @@ export default class PpParser {
} else {
const macro = this._definedMacros.get(id.lexeme);
if (!macro) {
// ParserUtils.throw(id.location, 'undefined macro:', id.lexeme);
return false;
}
if (macro.isFunction) {
ParserUtils.throw(id.location, "invalid function macro usage");
this.reportError(id.location, "invalid function macro usage", scanner.source, scanner.file);
}
const value = Number(macro.body.lexeme);
if (!Number.isInteger(value)) {
ParserUtils.throw(id.location, "invalid const macro:", id.lexeme);
this.reportError(id.location, `invalid const macro: ${id.lexeme}`, scanner.source, scanner.file);
}
this._branchMacros.add(id.lexeme);
return value;
@@ -434,7 +454,12 @@ export default class PpParser {
const integer = scanner.scanInteger();
return Number(integer.lexeme);
} else {
ParserUtils.throw(scanner.getShaderPosition(), "invalid token", scanner.getCurChar());
this.reportError(
scanner.getShaderPosition(),
`invalid token: ${scanner.getCurChar()}`,
scanner.source,
scanner.file
);
}
}
@@ -447,7 +472,7 @@ export default class PpParser {
parentScanner: PpScanner
): {
content: string;
// #if _EDITOR
// #if _VERBOSE
sourceMap: PpSourceMap;
// #endif
};
@@ -457,7 +482,7 @@ export default class PpParser {
file: string
): {
content: string;
// #if _EDITOR
// #if _VERBOSE
sourceMap: PpSourceMap;
// #endif
};
@@ -467,7 +492,7 @@ export default class PpParser {
scannerOrFile: PpScanner | string
): {
content: string;
// #if _EDITOR
// #if _VERBOSE
sourceMap: PpSourceMap;
// #endif
} {
@@ -482,7 +507,7 @@ export default class PpParser {
this._expandSegmentsStack.pop();
return {
content: ret,
// #if _EDITOR
// #if _VERBOSE
sourceMap: scanner.sourceMap
// #endif
};
@@ -501,12 +526,12 @@ export default class PpParser {
const end = nextDirective.type === EPpKeyword.endif ? scanner.getShaderPosition() : scanner.scanRemainMacro();
const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner);
// #if _EDITOR
// #if _VERBOSE
const blockInfo = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap);
// #endif
const range = ShaderLab.createRange(bodyChunk.location.start, end);
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block: blockInfo,
// #endif
rangeInBlock: range,
@@ -521,14 +546,14 @@ export default class PpParser {
}
private static _addEmptyReplace(scanner: PpScanner, start: number) {
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange);
// #endif
const startPosition = ShaderLab.createPosition(start);
const endPosition = scanner.curPosition;
const endPosition = scanner.getCurPosition();
const range = ShaderLab.createRange(startPosition, endPosition);
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
@@ -546,12 +571,12 @@ export default class PpParser {
if (!!constantExpr) {
const end = nextDirective.type === EPpKeyword.endif ? scanner.getShaderPosition() : scanner.scanRemainMacro();
const expanded = this._expandMacroChunk(bodyChunk.lexeme, bodyChunk.location, scanner);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap);
// #endif
const range = ShaderLab.createRange(bodyChunk.location.start, end);
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
@@ -571,7 +596,7 @@ export default class PpParser {
let end = macro.location.end;
if (this._definedMacros.get(macro.lexeme) && macro.lexeme.startsWith("GL_")) {
ParserUtils.throw(macro.location, "redefined macro:", macro.lexeme);
this.reportError(macro.location, `redefined macro: ${macro.lexeme}`, scanner.source, scanner.file);
}
let macroArgs: BaseToken[] | undefined;
@@ -584,15 +609,15 @@ export default class PpParser {
const macroDefine = new MacroDefine(macro, macroBody, range, macroArgs);
this._definedMacros.set(macro.lexeme, macroDefine);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange);
// #endif
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: ShaderLab.createRange(start, scanner.curPosition),
rangeInBlock: ShaderLab.createRange(start, scanner.getCurPosition()),
replace: ""
});
}
@@ -601,13 +626,13 @@ export default class PpParser {
const start = scanner.current - 6;
const macro = scanner.scanWord();
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange);
// #endif
const startPosition = ShaderLab.createPosition(start);
const range = ShaderLab.createRange(startPosition, scanner.curPosition);
const range = ShaderLab.createRange(startPosition, scanner.getCurPosition());
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,
@@ -659,15 +684,15 @@ export default class PpParser {
args.push(scanner.source.slice(curIdx, scanner.current));
scanner.advance();
const range = ShaderLab.createRange(token.location!.start, scanner.curPosition);
replace = macro.expand(...args);
const range = ShaderLab.createRange(token.location!.start, scanner.getCurPosition());
replace = macro.expandFunctionBody(args);
const expanded = this._expandMacroChunk(replace, range, scanner);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap);
// #endif
const blockRange = ShaderLab.createRange(token.location!.start, scanner.curPosition);
const blockRange = ShaderLab.createRange(token.location!.start, scanner.getCurPosition());
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: blockRange,
@@ -675,12 +700,12 @@ export default class PpParser {
});
} else {
const expanded = this._expandMacroChunk(replace, token.location, scanner);
// #if _EDITOR
// #if _VERBOSE
const block = new BlockInfo(scanner.file, scanner.blockRange, expanded.sourceMap);
// #endif
const range = ShaderLab.createRange(token.location.start, token.location.end);
this.expandSegments.push({
// #if _EDITOR
// #if _VERBOSE
block,
// #endif
rangeInBlock: range,

View File

@@ -1,11 +1,10 @@
import { ShaderRange, ShaderPosition } from "../common";
import LexerUtils from "../lexer/Utils";
// #if _EDITOR
// #if _VERBOSE
import PpSourceMap from "./sourceMap";
// #endif
import BaseScanner from "../common/BaseScanner";
import { BaseToken, EOF } from "../common/BaseToken";
import { ParserUtils } from "../Utils";
import { EPpKeyword, EPpToken, PpKeyword } from "./constants";
import { PpUtils } from "./Utils";
import { ShaderLab } from "../ShaderLab";
@@ -15,12 +14,9 @@ export type OnToken = (token: BaseToken, scanner: PpScanner) => void;
export default class PpScanner extends BaseScanner {
private static _splitCharacters = /[\w#.]/;
private line: number = 0;
private column: number = 0;
private macroLvl = 0;
// #if _EDITOR
// #if _VERBOSE
readonly sourceMap = new PpSourceMap();
readonly file: string;
readonly blockRange?: ShaderRange;
@@ -28,13 +24,13 @@ export default class PpScanner extends BaseScanner {
constructor(
source: string,
// #if _EDITOR
// #if _VERBOSE
file = "__main__",
blockRange?: ShaderRange
// #endif
) {
super(source);
// #if _EDITOR
// #if _VERBOSE
this.file = file;
this.blockRange = blockRange;
// #endif
@@ -90,7 +86,7 @@ export default class PpScanner extends BaseScanner {
const end = this._currentIndex;
const word = this._source.slice(start, end);
if (end === start) {
ParserUtils.throw(this.getShaderPosition(), "no word found.");
this.throwError(this.getShaderPosition(), "no word found.");
}
const kw = PpKeyword.get(word);
if (kw) {
@@ -105,7 +101,13 @@ export default class PpScanner extends BaseScanner {
}
getShaderPosition(offset /** offset from starting point */ = 0) {
return ShaderLab.createPosition(this._currentIndex - offset, this.line, this.column - offset);
return ShaderLab.createPosition(
this._currentIndex - offset,
// #if _VERBOSE
this.line,
this.column - offset
// #endif
);
}
/**
@@ -144,14 +146,14 @@ export default class PpScanner extends BaseScanner {
scanQuotedString(): BaseToken<EPpToken.string_const> {
this.skipSpace(true);
if (this.getCurChar() !== '"') {
ParserUtils.throw(this.getShaderPosition(), "unexpected char, expected '\"'");
this.throwError(this.getShaderPosition(), "unexpected char, expected '\"'");
}
const ShaderPosition = this.getShaderPosition();
this._advance();
const start = this._currentIndex;
while (this.getCurChar() !== '"' && !this.isEnd()) this._advance();
if (this.isEnd()) {
ParserUtils.throw(this.getShaderPosition(), "unexpected char, expected '\"'");
this.throwError(this.getShaderPosition(), "unexpected char, expected '\"'");
}
const word = this._source.slice(start, this._currentIndex);
@@ -233,7 +235,7 @@ export default class PpScanner extends BaseScanner {
this.advance();
}
if (this._currentIndex === start) {
ParserUtils.throw(this.getShaderPosition(), "no integer found");
this.throwError(this.getShaderPosition(), "no integer found");
}
const integer = this._source.slice(start, this._currentIndex);
@@ -296,7 +298,7 @@ export default class PpScanner extends BaseScanner {
while (this.getCurChar() !== "\n" && !this.isEnd()) {
this._advance();
}
return ShaderLab.createRange(start, this.curPosition);
return ShaderLab.createRange(start, this.getCurPosition());
} else if (this.peek(2) === "/*") {
const start = this.getShaderPosition();
// multi-line comments

View File

@@ -1,4 +1,4 @@
import PpParser from "./PpParser";
import { PpParser } from "./PpParser";
import PpScanner from "./PpScanner";
/** @internal */
@@ -16,7 +16,7 @@ export class Preprocessor {
/**
* Should call it after reset.
*/
static process(source: string): string {
static process(source: string): string | null {
this.baseScanner = new PpScanner(source);
return PpParser.parse(this.baseScanner);
}
@@ -25,7 +25,7 @@ export class Preprocessor {
PpParser.addPredefinedMacro(macro, value);
}
// #if _EDITOR
// #if _VERBOSE
static convertSourceIndex(index: number) {
return this.baseScanner.sourceMap.map(index);
}

View File

@@ -1,6 +1,6 @@
import { ShaderRange } from "../common";
import { ExpandSegment } from "./PpParser";
// #if _EDITOR
// #if _VERBOSE
import PpSourceMap, { MapRange } from "./sourceMap";
// #endif
@@ -8,7 +8,7 @@ export class PpUtils {
static expand(
segments: ExpandSegment[],
source: string,
// #if _EDITOR
// #if _VERBOSE
sourceMap?: PpSourceMap
//#endif
) {
@@ -22,7 +22,7 @@ export class PpUtils {
const generatedIdxEnd = generatedIdx + originSlice.length + seg.replace.length;
// #if _EDITOR
// #if _VERBOSE
const mapRange = new MapRange(seg.block, seg.rangeInBlock, {
start: generatedIdx + originSlice.length,
end: generatedIdxEnd

View File

@@ -1,6 +1,6 @@
import { ShaderRange } from "../../common/ShaderRange";
// #if _EDITOR
// #if _VERBOSE
export class BlockInfo {
readonly sourceFile: string;
readonly rangeInFile?: ShaderRange;

View File

@@ -0,0 +1,14 @@
{
"license": "MIT",
"main": "../dist/main.verbose.js",
"module": "../dist/module.verbose.js",
"browser": "../dist/browser.verbose.min.js",
"debug": "../src/index.ts",
"types": "../types/index.d.ts",
"umd": {
"name": "Galacean.ShaderLab",
"globals": {
"@galacean/engine": "Galacean"
}
}
}

View File

@@ -8,7 +8,6 @@ import serve from "rollup-plugin-serve";
import miniProgramPlugin from "./rollup.miniprogram.plugin";
import replace from "@rollup/plugin-replace";
import { swc, defineRollupSwcOption, minify } from "rollup-plugin-swc3";
import modify from "rollup-plugin-modify";
import jscc from "rollup-plugin-jscc";
const { BUILD_TYPE, NODE_ENV } = process.env;
@@ -26,6 +25,9 @@ const pkgs = fs
};
});
const shaderLabPkg = pkgs.find((item) => item.pkgJson.name === "@galacean/engine-shader-lab");
pkgs.push({ ...shaderLabPkg, verboseMode: true });
// toGlobalName
const extensions = [".js", ".jsx", ".ts", ".tsx"];
@@ -49,9 +51,6 @@ const commonPlugins = [
})
),
commonjs(),
jscc({
values: { _EDITOR: NODE_ENV !== "release" }
}),
NODE_ENV === "development"
? serve({
contentBase: "packages",
@@ -60,11 +59,19 @@ const commonPlugins = [
: null
];
function config({ location, pkgJson }) {
function config({ location, pkgJson, verboseMode }) {
const input = path.join(location, "src", "index.ts");
const dependencies = Object.assign({}, pkgJson.dependencies ?? {}, pkgJson.peerDependencies ?? {});
const curPlugins = Array.from(commonPlugins);
curPlugins.push(
jscc({
values: { _VERBOSE: verboseMode }
})
);
const external = Object.keys(dependencies);
commonPlugins.push(
curPlugins.push(
replace({
preventAssignment: true,
__buildVersion: pkgJson.version
@@ -76,16 +83,20 @@ function config({ location, pkgJson }) {
const umdConfig = pkgJson.umd;
let file = path.join(location, "dist", "browser.js");
const plugins = [
modify({
find: "chevrotain",
replace: path.join(process.cwd(), "packages", "shader-lab", `./node_modules/chevrotain/lib/chevrotain.js`)
}),
...commonPlugins
];
if (compress) {
plugins.push(minify({ sourceMap: true }));
file = path.join(location, "dist", "browser.min.js");
if (verboseMode) {
if (compress) {
curPlugins.push(minify({ sourceMap: true }));
file = path.join(location, "dist", "browser.verbose.min.js");
} else {
file = path.join(location, "dist", "browser.verbose.js");
}
} else {
if (compress) {
curPlugins.push(minify({ sourceMap: true }));
file = path.join(location, "dist", "browser.min.js");
} else {
file = path.join(location, "dist", "browser.js");
}
}
const umdExternal = Object.keys(umdConfig.globals ?? {});
@@ -102,17 +113,21 @@ function config({ location, pkgJson }) {
globals: umdConfig.globals
}
],
plugins
plugins: curPlugins
};
},
mini: () => {
const plugins = [...commonPlugins, ...miniProgramPlugin];
let file = path.join(location, "dist", "miniprogram.js");
const plugins = [...curPlugins, ...miniProgramPlugin];
if (verboseMode) {
file = path.join(location, "dist", "miniprogram.verbose.js");
}
return {
input,
output: [
{
format: "cjs",
file: path.join(location, "dist/miniprogram.js"),
file,
sourcemap: false
}
],
@@ -121,29 +136,28 @@ function config({ location, pkgJson }) {
};
},
module: () => {
const plugins = [
modify({
find: "chevrotain",
replace: path.join(process.cwd(), "packages", "shader-lab", `./node_modules/chevrotain/lib/chevrotain.js`)
}),
...commonPlugins
];
let esFile = path.join(location, pkgJson.module);
let mainFile = path.join(location, pkgJson.main);
if (verboseMode) {
esFile = path.join(location, "dist", "module.verbose.js");
mainFile = path.join(location, "dist", "main.verbose.js");
}
return {
input,
external,
output: [
{
file: path.join(location, pkgJson.module),
file: esFile,
format: "es",
sourcemap: true
},
{
file: path.join(location, pkgJson.main),
file: mainFile,
sourcemap: true,
format: "commonjs"
}
],
plugins
plugins: curPlugins
};
}
};

View File

@@ -3,7 +3,7 @@ import { testCaseList } from "./test-case";
import { ShaderLib } from "@galacean/engine-core";
import { expect } from "chai";
import { readFileSync } from "fs";
import { Preprocessor } from "@galacean/engine-shader-lab";
import { Preprocessor } from "@galacean/engine-shader-lab/verbose";
import { join } from "path";
const includedSource = readFileSync(join(__dirname, "test-case/included.txt")).toString();

View File

@@ -1,17 +1,10 @@
import {
BlendFactor,
BlendOperation,
CompareFunction,
CullMode,
RenderQueueType,
RenderStateDataKey,
StencilOperation
} from "@galacean/engine-core";
import { BlendOperation, CompareFunction, CullMode, RenderStateDataKey } from "@galacean/engine-core";
import { Color } from "@galacean/engine-math";
import { ShaderLab } from "@galacean/engine-shader-lab";
import { glslValidate } from "./ShaderValidate";
import { ShaderLab as ShaderLabVerbose, GSError } from "@galacean/engine-shader-lab/verbose";
import { ShaderLab as ShaderLabRelease } from "@galacean/engine-shader-lab";
import { glslValidate, shaderParse } from "./ShaderValidate";
import chai, { expect } from "chai";
import chai, { expect, assert } from "chai";
import spies from "chai-spies";
import fs from "fs";
import path from "path";
@@ -111,7 +104,8 @@ vec4 linearToGamma(vec4 linearIn){
#endif
`;
const shaderLab = new ShaderLab();
const shaderLabVerbose = new ShaderLabVerbose();
const shaderLabRelease = new ShaderLabRelease();
describe("ShaderLab", () => {
let shader: IShaderContent;
@@ -120,7 +114,7 @@ describe("ShaderLab", () => {
let pass1: IShaderContent["subShaders"][number]["passes"][number];
before(() => {
shader = shaderLab._parseShaderContent(demoShader);
shader = shaderLabVerbose._parseShaderContent(demoShader);
subShader = shader.subShaders[0];
passList = subShader.passes;
expect(passList[0].isUsePass).to.be.true;
@@ -129,7 +123,7 @@ describe("ShaderLab", () => {
});
it("create shaderLab", async () => {
expect(shaderLab).not.be.null;
expect(shaderLabVerbose).not.be.null;
});
it("shader name", () => {
@@ -188,68 +182,80 @@ describe("ShaderLab", () => {
});
it("engine shader", async () => {
glslValidate(demoShader, shaderLab);
glslValidate(demoShader, shaderLabVerbose);
glslValidate(demoShader, shaderLabRelease);
});
it("include", () => {
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/unlit.shader")).toString();
glslValidate(demoShader, shaderLab, { test_common: commonSource });
glslValidate(demoShader, shaderLabVerbose, { test_common: commonSource });
});
it("planarShadow shader", () => {
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/planarShadow.shader")).toString();
glslValidate(demoShader, shaderLab);
glslValidate(demoShader, shaderLabVerbose);
glslValidate(demoShader, shaderLabRelease);
});
it("Empty macro shader", () => {
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/triangle.shader")).toString();
glslValidate(demoShader, shaderLab);
glslValidate(demoShader, shaderLabVerbose);
glslValidate(demoShader, shaderLabRelease);
});
it("No frag shader args", () => {
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/noFragArgs.shader")).toString();
glslValidate(demoShader, shaderLab);
glslValidate(demoShader, shaderLabVerbose);
glslValidate(demoShader, shaderLabRelease);
});
it("water full shader(complex)", () => {
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/waterfull.shader")).toString();
glslValidate(demoShader, shaderLab);
glslValidate(demoShader, shaderLabVerbose);
glslValidate(demoShader, shaderLabRelease);
});
it("glass shader", () => {
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/glass.shader")).toString();
glslValidate(demoShader, shaderLab);
glslValidate(demoShader, shaderLabVerbose);
glslValidate(demoShader, shaderLabRelease);
});
// it("shader with duplicate name", () => {
// const demoShader = fs.readFileSync(path.join(__dirname, "shaders/glass.shader")).toString();
// (Shader as any)._shaderLab = shaderLab;
// const shaderInstance = Shader.create(demoShader);
// expect(shaderInstance).instanceOf(Shader);
// const errorSpy = chai.spy.on(console, "error");
// Shader.create(demoShader);
// expect(errorSpy).to.have.been.called.with('Shader named "Gem" already exists.');
// shaderInstance.destroy();
// chai.spy.restore(console, "error");
// const sameNameShader = Shader.create(demoShader);
// expect(sameNameShader).instanceOf(Shader);
// });
it("template shader", () => {
const demoShader = fs.readFileSync(path.join(__dirname, "shaders/template.shader")).toString();
glslValidate(demoShader, shaderLab);
glslValidate(demoShader, shaderLabVerbose);
glslValidate(demoShader, shaderLabRelease);
});
it("multi-pass", () => {
const shaderSource = fs.readFileSync(path.join(__dirname, "shaders/multi-pass.shader")).toString();
glslValidate(shaderSource, shaderLab);
glslValidate(shaderSource, shaderLabVerbose);
glslValidate(shaderSource, shaderLabRelease);
});
it("macro-with-preprocessor", () => {
const shaderSource = fs.readFileSync(path.join(__dirname, "shaders/macro-pre.shader")).toString();
glslValidate(shaderSource, shaderLab);
glslValidate(shaderSource, shaderLabVerbose);
glslValidate(shaderSource, shaderLabRelease);
});
it("compilation-error", () => {
const errorShader = fs.readFileSync(path.join(__dirname, "shaders/compilation-error.shader")).toString();
shaderParse.bind(shaderLabVerbose)(errorShader);
// @ts-ignore
expect(shaderLabVerbose.errors.length).to.eq(3);
// @ts-ignore
assert.instanceOf(shaderLabVerbose.errors[0], GSError);
// @ts-ignore
assert.instanceOf(shaderLabVerbose.errors[1], GSError);
// @ts-ignore
assert.instanceOf(shaderLabVerbose.errors[2], GSError);
// @ts-ignore
for (const err of shaderLabVerbose.errors) {
console.log(err.toString());
}
expect(shaderParse.bind(shaderLabRelease, errorShader)).to.throw(Error);
});
});

View File

@@ -1,6 +1,6 @@
import { expect } from "chai";
import { ShaderLab } from "@galacean/engine-shader-lab";
import { Shader, ShaderFactory, ShaderPass, ShaderPlatformTarget } from "@galacean/engine-core";
import { Shader, ShaderFactory, ShaderPass, ShaderPlatformTarget, ShaderMacro } from "@galacean/engine-core";
import { IShaderContent } from "@galacean/engine-design/src/shader-lab";
function addLineNum(str: string) {
@@ -94,3 +94,30 @@ export function glslValidate(shaderSource, _shaderLab?: ShaderLab, includeMap =
});
});
}
export function shaderParse(
shaderSource: string,
macros: ShaderMacro[] = [],
backend: ShaderPlatformTarget = ShaderPlatformTarget.GLES100
): (ReturnType<ShaderLab["_parseShaderPass"]> & { name: string })[] {
const structInfo = this._parseShaderContent(shaderSource);
const passResult = [] as any;
for (const subShader of structInfo.subShaders) {
for (const pass of subShader.passes) {
if (pass.isUsePass) continue;
const passInfo = this._parseShaderPass(
pass.contents,
pass.vertexEntry,
pass.fragmentEntry,
macros,
backend,
[],
// @ts-ignore
new URL(pass.name, ShaderPass._shaderRootPath).href
) as any;
passInfo.name = pass.name;
passResult.push(passInfo);
}
}
return passResult;
}

View File

@@ -0,0 +1,100 @@
Shader "custom/pbr" {
EditorProperties {
material_BaseColor("Main Color", Color) = (0, 0, 0, 1);
material_AlphaCutoff("Alpha Cutoff", Range(0, 1, 0.01)) = 0;
material_BaseTexture("Texture", Texture2D);
}
EditorMacros {
Header("Conditional Macors") {
MATERIAL_HAS_BASETEXTURE("Base Texture");
MATERIAL_IS_ALPHA_CUTOFF("Alpha Cutoff");
MATERIAL_IS_TRANSPARENT("Transparent");
}
}
SubShader "Default" {
Pass "Pass0" {
#ifdef MATERIAL_IS_TRANSPARENT
BlendState {
Enabled = true;
SourceColorBlendFactor = BlendFactor.SourceAlpha;
DestinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha;
SourceAlphaBlendFactor = BlendFactor.One;
DestinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha;
}
DepthState {
WriteEnabled = false;
}
RenderQueueType = Transparent;
#else
BlendState {
Enabled = false;
SourceColorBlendFactor = BlendFactor.SourceAlpha;
DestinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha;
SourceAlphaBlendFactor = BlendFactor.One;
DestinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha;
}
DepthState {
WriteEnabled = true;
}
RenderQueueType = Opaque;
#endif
mat4 renderer_MVPMat;
vec4 material_BaseColor;
float material_AlphaCutoff;
sampler2D material_BaseTexture;
struct Attributes {
vec4 POSITION;
vec2 TEXCOORD_0;
};
struct Varyings {
vec3 v_pos;
vec2 v_uv;
};
VertexShader = vert;
FragmentShader = frag;
Varyings vert(Attributes attr) {
Varyings v;
gl_Position = renderer_MVPMat * attr2.POSITION;
none(
12
);
v.v_pos = gl_Position.xyz;
v.v_uv = attr.TEXCOORD_023;
return v;
}
void frag(Varyings v) {
vec4 baseColor = material_BaseColor;
#ifdef MATERIAL_HAS_BASETEXTURE
vec4 textureColor = texture2D(material_BaseTexture, v.v_uv);
#ifndef ENGINE_IS_COLORSPACE_GAMMA
textureColor = gammaToLinear(textureColor);
#endif
baseColor *= textureColor;
#endif
#ifdef MATERIAL_IS_ALPHA_CUTOFF
if( baseColor.a < material_AlphaCutoff ) {
discard;
}
#endif
gl_FragColor = baseColor;
#ifndef MATERIAL_IS_TRANSPARENT
gl_FragColor.a = 1.0;
#endif
}
}
}
}

View File

@@ -75,8 +75,13 @@ Shader "Water" {
RenderQueueType = Opaque;
/* First comment */
/* Second comment */
#define SCENE_SHADOW_TYPE 3
/*Comment without leading space*/
v2f vert(a2v v) {
v2f o;
@@ -88,6 +93,9 @@ Shader "Water" {
return o;
}
/* This is a
multi-line comment */
void frag(v2f i) {
vec4 color = texture2D(material_BaseTexture, i.v_uv) * u_color;
float fogDistance = length(i.v_position);

View File

@@ -2,6 +2,14 @@ Shader "Triangle" {
SubShader "Default" {
mat4 renderer_MVPMat;
BlendState transparentBlendState {
Enabled = true;
SourceColorBlendFactor = BlendFactor.SourceAlpha;
DestinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha;
SourceAlphaBlendFactor = BlendFactor.One;
DestinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha;
}
struct a2v {
vec4 POSITION;
vec2 TEXCOORD_0;
@@ -52,6 +60,8 @@ Shader "Triangle" {
Pass "0" {
vec3 u_color;
BlendState = transparentBlendState;
struct a2v {
vec4 POSITION;
};
@@ -79,6 +89,8 @@ Shader "Triangle" {
Pass "1" {
vec3 u_color;
BlendState = transparentBlendState;
struct a2v {
vec4 POSITION;
};

View File

@@ -10,7 +10,8 @@ void Gerstner() {
// #include "ShadowCoord"
#define TT 1.0 // comments
#define TT 1./** comments
*/0 // test
#define QQ
vec4 a = vec4(TT,1.0,3.0,4.0);