forked from galacean/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathShaderTargetParser.ts
142 lines (127 loc) · 4.87 KB
/
ShaderTargetParser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import { Grammar } from "./Grammar";
import { ENonTerminal, GrammarSymbol } from "./GrammarSymbol";
import { BaseToken } from "../common/BaseToken";
import { ETokenType } from "../common";
import { EAction, StateActionTable, StateGotoTable } from "../lalr/types";
import { ASTNode, TreeNode } from "./AST";
import SematicAnalyzer from "./SemanticAnalyzer";
import { TraceStackItem } from "./types";
import { addTranslationRule, createGrammar } from "../lalr/CFG";
import { LALR1 } from "../lalr";
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
*/
export class ShaderTargetParser {
readonly actionTable: StateActionTable;
readonly gotoTable: StateGotoTable;
readonly grammar: Grammar;
readonly sematicAnalyzer: SematicAnalyzer;
private _traceBackStack: (TraceStackItem | number)[] = [];
private get curState() {
return this._traceBackStack[this._traceBackStack.length - 1] as number;
}
private get stateActionTable() {
return this.actionTable.get(this.curState)!;
}
private get stateGotoTable() {
return this.gotoTable.get(this.curState);
}
// #if _VERBOSE
/** @internal */
get errors() {
return this.sematicAnalyzer.errors;
}
// #endif
static _singleton: ShaderTargetParser;
static create() {
if (!this._singleton) {
const grammar = createGrammar();
const generator = new LALR1(grammar);
generator.generate();
this._singleton = new ShaderTargetParser(generator.actionTable, generator.gotoTable, grammar);
addTranslationRule(this._singleton.sematicAnalyzer);
}
return this._singleton;
}
private constructor(actionTable: StateActionTable, gotoTable: StateGotoTable, grammar: Grammar) {
this.actionTable = actionTable;
this.gotoTable = gotoTable;
this.grammar = grammar;
this.sematicAnalyzer = new SematicAnalyzer();
}
parse(tokens: Generator<BaseToken, BaseToken>): ASTNode.GLShaderProgram | null {
this.sematicAnalyzer.reset();
const start = performance.now();
const { _traceBackStack: traceBackStack, sematicAnalyzer } = this;
traceBackStack.push(0);
let nextToken = tokens.next();
let loopCount = 0;
while (true) {
loopCount += 1;
const token = nextToken.value;
const actionInfo = this.stateActionTable.get(token.type);
if (actionInfo?.action === EAction.Shift) {
traceBackStack.push(token, actionInfo.target!);
nextToken = tokens.next();
} else if (actionInfo?.action === EAction.Accept) {
Logger.info(
`[pass compilation - parser] Accept! State automata run ${loopCount} times! cost time ${
performance.now() - start
}ms`
);
sematicAnalyzer.acceptRule?.(sematicAnalyzer);
return sematicAnalyzer.semanticStack.pop() as ASTNode.GLShaderProgram;
} else if (actionInfo?.action === EAction.Reduce) {
const target = actionInfo.target!;
const reduceProduction = this.grammar.getProductionByID(target)!;
const translationRule = sematicAnalyzer.getTranslationRule(reduceProduction.id);
const values: (TreeNode | BaseToken)[] = [];
for (let i = reduceProduction.derivation.length - 1; i >= 0; i--) {
if (reduceProduction.derivation[i] === ETokenType.EPSILON) continue;
traceBackStack.pop();
const token = traceBackStack.pop();
if (token instanceof BaseToken) {
values.unshift(token);
} else {
const astNode = sematicAnalyzer.semanticStack.pop()!;
values.unshift(astNode);
}
}
translationRule?.(sematicAnalyzer, ...values);
const gotoTable = this.stateGotoTable;
traceBackStack.push(reduceProduction.goal);
const nextState = gotoTable?.get(reduceProduction.goal)!;
traceBackStack.push(nextState);
continue;
} else {
const error = ShaderLabUtils.createGSError(
`Unexpected token ${token.lexeme}`,
GSErrorName.CompilationError,
ShaderLab._processingPassText,
token.location
);
// #if _VERBOSE
this.sematicAnalyzer.errors.push(error);
// #endif
return null;
}
}
}
// #if _VERBOSE
private _printStack(nextToken: BaseToken) {
let str = "";
for (let i = 0; i < this._traceBackStack.length - 1; i++) {
const state = <ENonTerminal>this._traceBackStack[i++];
const token = this._traceBackStack[i];
str += `State${state} - ${(<BaseToken>token).lexeme ?? ParserUtils.toString(token as GrammarSymbol)}; `;
}
str += `State${this._traceBackStack[this._traceBackStack.length - 1]} --- ${nextToken.lexeme}`;
Logger.info(str);
}
// #endif
}