costreamjs
Version:
A high-performance streaming programming language for parallel architecture. This repo (js-version) is created for better using & reading & debugging.
346 lines (319 loc) • 13.9 kB
JavaScript
import { SymbolTable, Variable, symbolTableList} from "./symbol";
import { declareNode, compositeNode, function_definition, expNode, blockNode, whileNode, forNode, unaryNode, ternaryNode, parenNode, castNode, constantNode, doNode, splitjoinNode, pipelineNode, compositeCallNode, strdclNode, binopNode,operatorNode, inOutdeclNode, callNode, selection_statement,addNode,operNode, sequentialNode, layerNode, fileReaderNode, matrix_constant} from "../ast/node";
import { deepCloneWithoutCircle, error } from "../utils";
import { matrix_section } from "../ast/node";
import { BUILTIN_FUNCTIONS, BUILTIN_MATRIX_FUNCTIONS, BUILTIN_FUNCTIONS_ARG, getMostNearName } from "./built-in-function";
import { fileWriterNode } from "../ast/node";
import { checkShape } from "./checkShape"
import { top,setTop, COStreamJS } from './global'
let saved = [];
let isInOperator = false; // 判断当前处理的二元节点是否正处于operator体内的上下文中, 若处于, 则检查shape且不修改符号表中的值. 若不处于, 则允许修改符号表中的值
function EnterScopeFn(/** @type {YYLTYPE}*/loc){
saved.push(top);
setTop(new SymbolTable(top,loc));
}
function ExitScopeFn(){
setTop(saved.pop());
}
/**
* 生成全局根符号表, 包括全局变量/composite表/函数表, 不深入composite内部(深入composite内部的符号表构建放在ast2ssg中完成)
*/
export function generateSymbolTables(program){
let S = new SymbolTable();
S.loc = {first_line:0,last_line:Infinity};
symbolTableList.length = 0; // 清空旧的符号表(当程序重复执行时可能会遇到符号表 List 不为空的情况)
symbolTableList.push(S)
setTop(S);
program.forEach(node => {
if(node instanceof declareNode){
generateDeclareNode(node);
}
else if(node instanceof compositeNode){
top.InsertCompositeSymbol(node);
}
else if(node instanceof function_definition){
console.warn("目前未支持函数符号表")
}
});
return symbolTableList;
}
function generateDeclareNode(/** @type{declareNode} */node){
node.init_declarator_list.forEach(init_node=>{
const name = init_node.identifier.name
generateStmt(init_node.initializer)
const variable = new Variable(node.type,name,init_node.initializer,node._loc);
if(node.type === "Matrix"){
variable.shape = checkShape(init_node.initializer, init_node._loc).map(x=>x.value)
}
top.InsertIdentifySymbol(variable);
})
}
let lastLoc = null
const ignoreTypes = [ternaryNode, parenNode, castNode, constantNode, fileReaderNode, fileWriterNode, matrix_constant]
// 解析 语句
function generateStmt(/** @type {Node} */stmt) {
if(!stmt) return
lastLoc = stmt instanceof Node ? stmt._loc : lastLoc //记录最近一次处理的节点的loc信息
switch (stmt.constructor) {
case Number: break;
case String: {
if (/[_A-z][_A-z0-9]*/.test(stmt) && !top.searchName(stmt)){ throw new Error(error(lastLoc,`在当前符号表链中未找到${stmt}的定义`, top))}
break;
}
case declareNode: {
if (stmt.type instanceof strdclNode) {
generateStrDlcNode(stmt);
} else {
generateDeclareNode(stmt);
}
break;
}
case matrix_section: if(isInOperator && COStreamJS.plugins.matrix) checkShape(stmt); break
case unaryNode: if(isInOperator && COStreamJS.plugins.matrix) checkShape(stmt); break
case binopNode: {
/**
* 对赋值语句有两种上下文需要处理
* 1. operator内,此时数据的变化发生在运行时, 因此只做shape检查和变量名是否存在的校验
* 2. composite内operator外, 此时变量名N的变化可能会影响到param数值,因此需要修改符号表内的数 */
if(isInOperator){
if(COStreamJS.plugins.matrix){
checkShape(stmt)
}else{
}
}else{
if (stmt.op === '=' && stmt.left instanceof String && stmt.right instanceof expNode) {
let variable = top.LookupIdentifySymbol(stmt.left);
variable.value = right.value;
}
generateStmt(stmt.left)
generateStmt(stmt.right)
}
break;
}
case operatorNode: {
top.InsertOperatorSymbol(stmt.operName, stmt);
EnterScopeFn(stmt._loc);
isInOperator = true
generateOperatorNode(stmt); //解析 operator 节点
isInOperator = false;
ExitScopeFn();
break;
}
case blockNode: {
stmt.stmt_list.forEach(st => generateStmt(st)) // 深入 { 代码块 } 内部进行遍历
break;
}
case whileNode: {
EnterScopeFn(stmt._loc);
generateStmt(stmt.exp);
generateStmt(stmt.statement);
ExitScopeFn();
break;
}
case forNode: {
EnterScopeFn(stmt._loc);
const { init, cond, next, statement } = stmt;
[init, cond, next, statement].forEach(generateStmt)
ExitScopeFn();
break;
}
case doNode: {
EnterScopeFn(stmt);
generateStmt(stmt.exp);
generateStmt(stmt.statement);
ExitScopeFn();
break;
}
case selection_statement: {
/** FIXME 暂未处理分支预测 */
break;
}
case callNode: {
if(isInOperator && COStreamJS.plugins.matrix){
checkShape(stmt);
return
}
if(typeof stmt.name === "string"){
if(BUILTIN_FUNCTIONS.includes(stmt.name)){
const wanted_args = BUILTIN_FUNCTIONS_ARG[stmt.name].length
if(wanted_args !== 'any' && wanted_args !== stmt.arg_list.length){
const hint = BUILTIN_FUNCTIONS_ARG[stmt.name].hint
throw new Error(error(stmt._loc, `调用函数${stmt.name}传参数量错误,当前传参为${stmt.arg_list},期待传参为${hint}`))
}
}
else{
const msg = `你是否想使用函数 ${getMostNearName(BUILTIN_FUNCTIONS,stmt.name)} ?`
throw new Error(error(stmt._loc, `不支持的函数调用 ${stmt.name},${msg} `))
}
}
/**
* 暂未支持用户自定义函数
*/
break;
}
case splitjoinNode: generateSplitjoin(stmt); break;
case pipelineNode: generatePipeline(stmt); break;
case sequentialNode: generateSequential(stmt); break;
case addNode: generateStmt(stmt.content); break
case compositeCallNode: {
/** 检查传入的参数是否存在 以及 获得参数值 FIXME */
if(! symbolTableList[0].compTable[stmt.compName]){
throw new Error(error(stmt._loc, `此处调用的 composite 未定义:${stmt.compName}`))
}
break
}
case Array: stmt.forEach(stmt => generateStmt(stmt)); break;
default: {
if (ignoreTypes.some(ignoreType => stmt instanceof ignoreType)) {
/** 这些类型不需要在生成符号表这一步进行处理,可暂时跳过 */
} else {
console.warn(`[generateStmt] FIXME: 暂未识别的 stmt 类型 ${stmt.constructor.name}`)
}
}
}
}
// 处理 stream 声明变量的语句
function generateStrDlcNode(/** @type {declareNode}*/ decl){ //stream "<int x,int y>" 这部分
decl.init_declarator_list.forEach( identifier_name => {
let stream_dlc = new inOutdeclNode();
stream_dlc.strType = decl.type
stream_dlc.id = identifier_name
stream_dlc._loc = decl._loc
top.InsertStreamSymbol(stream_dlc)
})
}
function generateOperatorNode(/** @type {operatorNode}*/oper){
oper._symbol_table = top
let inputs = oper.inputs;
let outputs = oper.outputs;
let body = oper.operBody
const checkStreamId = name => {
if(! top.searchName(name) || top.searchName(name).type !== 'stream'){
throw new Error(`当前 operator: ${oper.operName} 相关的流 ${name} 在作用域中未声明`)
}
}
inputs && inputs.forEach(checkStreamId);
outputs && outputs.forEach(checkStreamId);
if(body){
if(body.stmt_list){
body.stmt_list.forEach(decl=>{
decl instanceof declareNode ? top.InsertMemberSymbol(decl)
:console.warn("[generateOperatorNode] 目前 operator 内部仅支持声明成员变量")
})
}
if(body.init){
EnterScopeFn(body.init._loc);
generateStmt(body.init)
body.init._symbol_table = top
ExitScopeFn();
}
if(body.work){
EnterScopeFn(body.work._loc);
generateStmt(body.work)
body.work._symbol_table = top
ExitScopeFn();
}
if(body.window){
body.window.forEach(winStmt =>checkStreamId(winStmt.winName))
}
}
}
// 解析 splitjoin
function generateSplitjoin(/** @type {splitjoinNode} */ splitjoin){
const checkStreamId = name => {
if(! top.searchName(name) || top.searchName(name).type !== 'stream'){
throw new Error(`当前 operator: ${splitjoin.compName} 相关的流 ${name} 在作用域中未声明`)
}
}
;(splitjoin.inputs||[]).forEach(checkStreamId)
;(splitjoin.outputs||[]).forEach(checkStreamId)
;(splitjoin.stmt_list||[]).forEach(generateStmt)
;(splitjoin.body_stmts||[]).forEach(generateStmt)
if(splitjoin.split){
// 保证参数列表中不出现未声明的字符
(splitjoin.split.arg_list||[]).forEach(generateStmt)
}
if(splitjoin.join){
(splitjoin.join.arg_list||[]).forEach(generateStmt)
}
}
// 解析 pipeline 节点
function generatePipeline(/** @type {pipelineNode} */pipe){
const checkStreamId = name => {
if(! top.searchName(name) || top.searchName(name).type !== 'stream'){
throw new Error(`当前 operator: ${pipe.compName} 相关的流 ${name} 在作用域中未声明`)
}
}
;(pipe.inputs||[]).forEach(checkStreamId)
;(pipe.outputs||[]).forEach(checkStreamId)
;(pipe.body_stmts||[]).forEach(generateStmt)
}
// 解析 sequential
function generateSequential(/** @type {sequentialNode} */ sequential){
const checkStreamId = name => {
if(! top.searchName(name) || top.searchName(name).type !== 'stream'){
throw new Error(`当前 operator: ${splitjoin.compName} 相关的流 ${name} 在作用域中未声明`)
}
}
;(sequential.inputs||[]).forEach(checkStreamId)
;(sequential.outputs||[]).forEach(checkStreamId)
;(sequential.body_stmts||[]).forEach(add =>{
if(add instanceof addNode && add.content instanceof layerNode){
return // 正确的情况
}else{
error(add._loc, "sequential 结构内部仅能添加以下几种 layerNode之一: Dense Conv2D MaxPooling2D AveragePooling2D")
}
})
if(sequential.body_stmts && sequential.body_stmts.length < 2){
error(sequential._loc, "sequential 结构中必须至少有 2 个 layer")
}
}
/**
*
* @param {operNode} call
* @param {compositeNode} composite
* @param {number[]} params
*/
export function generateCompositeRunningContext(call,composite,params=[]){
setTop(new SymbolTable(top, composite._loc))
if(!composite.body) throw new Error(error(call._loc, `调用的${composite.compName}没有body,编译中止`))
composite._symbol_table = top;
isInOperator = false;
// 第一步 解析 param
let param = composite.body.param;
const paramNames = []
if(param && param.param_list){
(param.param_list|| []).forEach((decl,index) => {
const name = decl.identifier.name
const variable = new Variable(decl.type,name,params[index],decl._loc);
top.InsertIdentifySymbol(variable)
paramNames.push(decl.identifier.name)
})
}
top.paramNames = paramNames
// 第二步, 处理 inputs 和 outputs
// 例子 composite Test(input stream<int x>In1, output stream<int x>Out1, stream<int x>Out2)
if(composite.inout){
composite.inout.input_list.forEach((inDecl, inIndex) => {
let prevStream = top.prev.streamTable[call.inputs[inIndex]]
const isTypeOK = JSON.stringify(prevStream.strType) == JSON.stringify(inDecl.strType);
if(isTypeOK){
top.streamTable[inDecl.id] = prevStream
}else{
throw new Error(error(call._loc, `调用${composite.compName}时输入流类型与定义不吻合`))
}
})
composite.inout.output_list.forEach((outDecl, outIndex) => {
let prevStream = top.prev.streamTable[call.outputs[outIndex]]
const isTypeOK = JSON.stringify(prevStream.strType) == JSON.stringify(outDecl.strType);
if(isTypeOK){
top.streamTable[outDecl.id] = prevStream
}else{
throw new Error(error(call._loc, `调用${composite.compName}时输出流类型与定义不吻合`))
}
})
}
// 第三步, 确认该composite的param和输入输出流都没问题后, 开始解析其body
composite.body.stmt_list.forEach(stmt => generateStmt(stmt))
return top
}