costreamjs
Version:
A high-performance streaming programming language for parallel architecture. This repo (js-version) is created for better using & reading & debugging.
330 lines (322 loc) • 17.2 kB
JavaScript
import { binopNode } from "../ast/node"
import { error } from "../utils"
import { top } from "./global"
import { ternaryNode } from "../ast/node";
import { matrix_section, callNode } from "../ast/node";
import { matrix_constant } from "../ast/node";
import { BUILTIN_FUNCTIONS, BUILTIN_MATRIX_FUNCTIONS, BUILTIN_FUNCTIONS_ARG, getMostNearName, BUILTIN_MATRIX_FUNCTIONS_ARG, BUILTIN_MATRIX_STATIC_FUNCTIONS_ARG } from "./built-in-function";
import { constantNode, parenNode, lib_binopNode, unaryNode } from "../ast/node";
let lastLoc = 0; //标记最近检查中处理到的最后一个行号,用于识别只有一个string的场景, 由于string类型无法得知自己的行号,因此需要从外部记忆
/** 该文件的函数同时执行两项工作: 对被操作数据的 shape 进行校验, 并将计算后的结果的 shape 缓存下来 */
export function checkShape(/** @type {Node | string} */stmt, _loc){
lastLoc = _loc || stmt._loc || lastLoc
let returnShape
if(Array.isArray(stmt)){
const itemShape = checkShape(stmt[0])
returnShape = [stmt.length].concat(itemShape == "1,1" ? 1 : itemShape)
}else if(stmt instanceof binopNode){
returnShape = checkBinopShape(stmt)//二元节点
}else if(stmt instanceof ternaryNode){
returnShape = checkTernaryNode(stmt)//三元节点
}else{
returnShape = checkUnaryShape(stmt) //一元节点
}
top.shapeCache.set(stmt, returnShape)
return returnShape
}
function checkBinopShape(/** @type {binopNode} */stmt){
if(stmt.op === '.'){
return checkDotShape(stmt);
}
if(stmt.op === '='){ // A = m 或 S[0].x = 1
return checkAssignmentShape(stmt.left, stmt.right)
}else if(['+','-','/','%','|','&','^','<','>','<=','==','>=','<<=','>>=','!='].includes(stmt.op)){
const lshape = checkShape(stmt.left), rshape = checkShape(stmt.right)
return checkEqualShape(lshape,rshape,stmt._loc)
}else if(stmt.op.length === 2 && stmt.op.right == '='){ // += -= *= /= %= 的情况
return checkAssignmentShape(stmt.left,checkEqualShape(lshape,rshape,stmt._loc))
}else if(stmt.op === '*'){
return checkMultiShape(stmt)
}
return [1,1]
}
function checkMultiShape(/** @type {binopNode} */stmt){
const lshape = checkShape(stmt.left), rshape = checkShape(stmt.right)
if(lshape == "1,1") return rshape
if(rshape == "1,1") return lshape
if(lshape.length == 2 && rshape.length == 2 && lshape[1] == rshape[0]){
return [lshape[0], rshape[1]]
}else{
throw new Error(error(stmt._loc,`乘法类型检查出错,左侧shape为${lshape}右侧shape为${rshape}`))
}
}
function checkDotShape(/** @type {binopNode} */stmt){
// S[0].x的情况
if(stmt.left instanceof matrix_section && typeof stmt.right === "string"){
const result = top.searchName(stmt.left.exp)
// 数据流 S[0].x的情况
if(result && result.type === 'stream'){
const id_list = result.origin.streamTable[stmt.left.exp].strType.id_list
const member = id_list.find(record => record.identifier === stmt.right)
if(!member){
throw new Error(error(stmt._loc,`数据流${stmt.left.exp}上不存在成员${stmt.right}`));
}
if(member.type !== 'Matrix'){
return [1,1]
}else{
return member.shape || undefined
}
}
if(!result || result.type !== 'stream'){
throw new Error(error(stmt._loc,`在符号表中找不到流${stmt.left.exp}`))
}
}else{
}
}
/**
* @returns {Array<{type:string,identifier:string,shape?:[1,1]}>}
*/
function checkUnaryShape(/** @type {Node} */stmt){
if(stmt instanceof matrix_section){
const lshape = checkShape(stmt.exp)
return checkSliceShape(lshape, stmt, stmt._loc)
}else if(stmt instanceof matrix_constant){
return stmt.shape
}else if(typeof stmt === 'string'){
if(!/[_A-z0-9]+/.test(stmt)) return [1,1]//若非标识符,直接返回
const result = top.searchName(stmt)
if(!result) throw new Error(error(lastLoc,`找不到变量名${stmt}在符号表中的定义`))
const { type, origin } = result
if(type === "variable"){
return origin.variableTable[stmt].shape
}else if(type === "member"){
return origin.memberTable[stmt].shape
}else{
throw new Error(error(lastLoc,`获取符号表中${stmt}的shape出错`))
}
}else if(stmt instanceof callNode){
return checkCallNodeShape(stmt)
}else if(stmt instanceof parenNode){
return checkShape(stmt.exp)
}else if(stmt instanceof constantNode){
return [1,1] //常数节点
}else if(stmt instanceof unaryNode){
if(['++','--','+','-','!','~'].includes(stmt.first)){
const rshape = checkShape(stmt.second)
const MatrixUnaryError = rshape.join('') !== '11' && stmt.first !== '+' && stmt.first !== '-' // 矩阵变量使用+-以外的前缀均错误
const ConstantUnaryError = stmt.second instanceof constantNode && (stmt.first === '++' || stmt.first === '--') // ++1 错误
if(MatrixUnaryError || ConstantUnaryError){
throw new Error(error(stmt._loc,`该变量${stmt.second}不能使用前缀操作符${stmt.first}`))
}
debugger;
return rshape
}else if(['++','--'].includes(stmt.second)){
const lshape = checkShape(stmt.first)
const MatrixUnaryError = lshape.join('') !== '11' // 该情况右侧只能为 ++ 或 --, 而矩阵变量不能这样做
const ConstantUnaryError = stmt.first instanceof constantNode // 1++ 0-- 也不对
if(MatrixUnaryError || ConstantUnaryError){
throw new Error(error(stmt._loc,`不能对${stmt.first}使用后缀操作符${stmt.second}`))
}
return lshape
}
}else{
console.warn("返回了一个shape [1,1]", stmt)
return [1,1]
}
}
function checkCallNodeShape(/** @type {callNode} */node){
if(typeof node.name === "string"){ //若为直接执行一个函数, 一般为数学函数
if(BUILTIN_FUNCTIONS.includes(node.name)){
const wanted_args = BUILTIN_FUNCTIONS_ARG[node.name].length
if(wanted_args !== 'any' && wanted_args !== node.arg_list.length){
const hint = BUILTIN_FUNCTIONS_ARG[node.name].hint
throw new Error(error(node._loc, `调用函数${node.name}传参数量错误,当前传参为${node.arg_list},期待传参为${hint}`))
}
node.arg_list.forEach(arg=>checkShape(arg,node._loc))
return [1,1] //全部检查通过, 因此该数学计算得到的值的结果是个数字, 返回数字的shape [1,1]
}
else{
const msg = `你是否想使用函数 ${getMostNearName(BUILTIN_FUNCTIONS,node.name)} ?`
throw new Error(error(node._loc, `不支持的函数调用 ${node.name},${msg} `))
}
}
else if(node.name instanceof binopNode){ // S.exp() 此类矩阵实例上执行函数
const funcName = node.name.right
if(typeof funcName === 'string' && BUILTIN_MATRIX_FUNCTIONS.includes(funcName)){
const wanted_args = BUILTIN_MATRIX_FUNCTIONS_ARG[funcName].length
if(wanted_args !== 'any' && wanted_args !== node.arg_list.length){
const hint = BUILTIN_MATRIX_FUNCTIONS_ARG[funcName].hint
throw new Error(error(node._loc, `调用矩阵函数${funcName}传参数量错误,当前传参为(${node.arg_list}),提示: ${hint}`))
}
const returnShape = BUILTIN_MATRIX_FUNCTIONS_ARG[funcName].returnShape
if(Array.isArray(returnShape)) return returnShape
else if(typeof returnShape === 'function'){
const lshape = checkShape(node.name.left)
return returnShape(lshape,node.arg_list,node._loc)
}
}else{
const mostNearName = getMostNearName(BUILTIN_MATRIX_FUNCTIONS,funcName)
const msg = `你是否想使用函数 ${mostNearName} ? hint:${BUILTIN_MATRIX_FUNCTIONS_ARG[mostNearName].hint}`
throw new Error(error(node._loc, `不支持的矩阵函数调用 ${funcName},${msg}`))
}
}else if(node.name instanceof lib_binopNode){ // Matrix.zeros(1,1) 矩阵生成函数调用
const funcName = node.name.function_name
const wanted = BUILTIN_MATRIX_STATIC_FUNCTIONS_ARG[funcName]
if(wanted){
if(wanted.length !== node.arg_list.length){
const hint = BUILTIN_MATRIX_STATIC_FUNCTIONS_ARG[funcName].hint
throw new Error(error(node._loc, `调用矩阵函数${funcName}传参数量错误,当前传参为(${node.arg_list}),提示: ${hint}`))
}
return wanted.returnShape(node.arg_list).map(x=>x.value)
}else{
const mostNearName = getMostNearName(BUILTIN_MATRIX_STATIC_FUNCTIONS,funcName)
const msg = `你是否想使用矩阵构造函数 ${mostNearName} ? hint:${BUILTIN_MATRIX_STATIC_FUNCTIONS_ARG[mostNearName].hint}`
throw new Error(error(node._loc, `不支持的矩阵函数调用 ${funcName},${msg}`))
}
}else{
throw new Error(error(node._loc, `未识别的callNode类型`))
}
}
/**
* 获取右侧矩阵或数组表达式的shape
* @returns {[number,number]}
*/
export function checkSliceShape(shape, /** @type {matrix_section} */matrix_s){
const slice_pair_list = matrix_s.slice_pair_list
let resShape = [], i
for(i=0;i<slice_pair_list.length; i++){
if(! slice_pair_list[i].op){
// 没有冒号:的情况, 即 S[0] 或 S[i,j], 直接降维
let start = (slice_pair_list[i].start || 0).value
if(start < 0) throw new Error(error(matrix_s._loc,`切片操作第${i}维的坐标不能小于0`))
if(start >= shape[i]) throw new Error(error(matrix_s._loc,`切片操作的第${i}维坐标不能大于等于最大值${shape[i]},当前为${start}`))
resShape.push(1)
}else{
// 有冒号的情况 , S[start:end]
let start = (slice_pair_list[i].start || 0).value
let end = (slice_pair_list[i].end || shape[i]).value
if(start < 0) throw new Error(error(matrix_s._loc,`切片操作第${i}维的起始坐标不能小于0`))
if(start >= shape[i]) throw new Error(error(matrix_s._loc,`切片操作的第${i}维起始坐标不能大于等于最大值${shape[i]},当前为${start}`))
if(end > shape[i]) throw new Error(error(matrix_s._loc,`切片操作的第${i}维终止坐标不能大于最大值${shape[i]},当前为${end}`))
resShape.push(end - start)
}
}
// 考虑对[5,6]矩阵取S[0:1]的情况,需保留尾部
if(i<shape.length){
resShape = resShape.concat(shape.slice(i))
}
// 移除左侧多余的1
while(resShape.length > 2 && resShape[0] === 1){
resShape = resShape.slice(1)
}
return resShape
}
function checkTernaryNode(/** @type {ternaryNode} */stmt){
return checkEqualShape(checkShape(stmt.second), checkShape(stmt.third), stmt._loc)
}
function checkAssignmentShape(left,right){
// x = 1 的情况
if(typeof left === 'string'){
const rshape = checkShape(right)
const result = top.searchName(left)
if(!result){
throw new Error(error(right._loc,`在符号表中找不到变量名${left}`));
}else if(result.type === 'member'){
// member成员只支持非矩阵数据
if(rshape.join('') !== '11'){
throw new Error(error(right._loc,`oper的成员${left}不支持赋值为${rshape.join('x')}大小的矩阵`));
}
return [1,1]
}else if(result.type === 'variable'){
const originVariable = result.origin.variableTable[left]
if(originVariable.shape.join() !== rshape.join()){
throw new Error(error(right._loc,`赋值语句左右两侧的shape不符,左侧为${originVariable.shape}右侧为${rshape}`));
}
return rshape; // 赋值表达式的shape检查通过, 返回该shape
}else{
throw new Error(error(right._loc,`该字段${left}不支持赋值`));
}
}else if(left instanceof matrix_section){
if(typeof left.exp === 'string'){
const result = top.searchName(left.exp)
if(result){
// 少见的一种情况, S[0] = In[0] 用于数据流的拷贝
if(result.type === 'stream' && right instanceof matrix_constant){
if(typeof right.exp === 'string'){
const right_result = top.searchName(right.exp)
if(right_result && right_result.type === 'stream'){
return [1,1] // 两侧均为数据流, 检查通过. 返回一个无意义的[1,1]
}
}else{
throw new Error(error(left._loc,`该行操作不合法`));
}
}else if(result.type === "member"){
// this.coeff[0][1] = 1 的情况
const lshape = result.origin.memberTable[left.exp].shape
return checkEqualShape(checkSliceShape(lshape,left), checkShape(right), left._loc)
}else if(result.type === "variable"){
// A[0] = 1 的情况
const lshape = result.origin.variableTable[left.exp].shape
return checkEqualShape(checkSliceShape(lshape,left), checkShape(right), left._loc)
}
throw new Error(error(left._loc,`该行操作不合法`));
}else{
throw new Error(error(_loc,`在符号表中找不到流${left.exp}`))
}
}
else{
if(left.exp instanceof binopNode && left.exp.left instanceof matrix_section){
// S[0].x[0,0] = 1 的情况
const lshape = checkShape(left.exp)
if(lshape.join('') !== '11'){
if(left.slice_pair_list.length < 2){
throw new Error(error(left._loc,`矩阵数据取下标需写全行列号`));
}
if(left.slice_pair_list[0].op || left.slice_pair_list[1].op){
throw new Error(error(left._loc,`矩阵在等号左边时暂不支持切片赋值[:,:], 只支持定点赋值[i,j]`));
}
}
return [1,1]
}
throw new Error(error(left._loc,`暂未支持该左操作数格式${left}`));
}
}else if(left instanceof binopNode && left.op == '.' && left.left instanceof matrix_section && typeof left.right ==='string'){
// S[0].x = 1 的情况
const matrix_s = left.left , _loc = left._loc
const result = top.searchName(matrix_s.exp)
if(result && result.type === 'stream'){
const id_list = result.origin.streamTable[matrix_s.exp].strType.id_list
const member = id_list.find(record => record.identifier === left.right)
if(!member){
throw new Error(error(_loc,`数据流${matrix_s.exp}上不存在成员${left.right}`));
}
const rshape = checkShape(right)
if(member.type !== 'Matrix'){
if(rshape.join('')!=='11') throw new Error(error(_loc,`不能给shape为1,1的值赋值新shape ${rshape}`));
return [1,1]
}else{
if(!member.shape){
return member.shape = rshape // 若该数据流的该字段是矩阵类型且未定义shape,则为其定义shape
}else{
if(member.shape.join() !== rshape.join()){
throw new Error(error(_loc,`不能给shape为${member.shape}的值赋值新shape ${rshape}`));
}
return member.shape // 若该数据流的该字段是矩阵类型且校验通过,则返回该shape
}
}
}else{
throw new Error(error(_loc,`在符号表中找不到流${matrix_s.exp}`))
}
}
throw new Error(error(right._loc," = 左侧的表达式不支持赋值"))
}
function checkEqualShape(/** @type {[number,number]} */lshape,/** @type {[number,number]} */rshape,_loc){
if(lshape[0] === rshape[0] && lshape[1] === rshape[1]){
return lshape //检测左右两侧的shape相同,通过检查
}else if(lshape.join('') !== '11' && rshape.join('') == '11'){
return lshape //矩阵和常数的加法,例如A+1
}else if(lshape.join('') === '11' && rshape.join('') !== '11'){
return rshape //常数和矩阵的加法,例如1+A
}
throw new Error(error(_loc, `左右操作数的shape未通过校验,左侧为${lshape},右侧为${rshape}`))
}