mirror of
https://github.com/galacean/engine.git
synced 2026-06-02 00:31:57 +08:00
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:
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"types/**/*"
|
||||
"types/**/*",
|
||||
"verbose/package.json"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@galacean/engine-design": "workspace:*",
|
||||
|
||||
@@ -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
|
||||
67
packages/shader-lab/src/GSError.ts
Normal file
67
packages/shader-lab/src/GSError.ts
Normal 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"
|
||||
}
|
||||
@@ -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 = "";
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
37
packages/shader-lab/src/ShaderLabUtils.ts
Normal file
37
packages/shader-lab/src/ShaderLabUtils.ts
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { CodeGenVisitor } from "./CodeGenVisitor";
|
||||
import { GLESVisitor } from "./GLESVisitor";
|
||||
import { VisitorContext } from "./VisitorContext";
|
||||
import { ICodeSegment } from "./types";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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] ?? "");
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// #if _EDITOR
|
||||
// #if _VERBOSE
|
||||
export * from "./functions";
|
||||
export * from "./variables";
|
||||
// #endif
|
||||
|
||||
@@ -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)];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ShaderRange } from "../../common/ShaderRange";
|
||||
|
||||
// #if _EDITOR
|
||||
// #if _VERBOSE
|
||||
export class BlockInfo {
|
||||
readonly sourceFile: string;
|
||||
readonly rangeInFile?: ShaderRange;
|
||||
|
||||
14
packages/shader-lab/verbose/package.json
Normal file
14
packages/shader-lab/verbose/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
100
tests/src/shader-lab/shaders/compilation-error.shader
Normal file
100
tests/src/shader-lab/shaders/compilation-error.shader
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user