costreamjs
Version:
A high-performance streaming programming language for parallel architecture. This repo (js-version) is created for better using & reading & debugging.
280 lines (261 loc) • 11.1 kB
JavaScript
import { Node,blockNode, idNode, ternaryNode,forNode, declareNode, declarator, binopNode, unaryNode, callNode, selection_statement, castNode, parenNode, matrix_section, matrix_constant, lib_binopNode, whileNode, doNode,matrix_slice_pair} from "./node.js"
import { BUILTIN_MATH } from "../FrontEnd/built-in-function"
import { COStreamJS, top} from "../FrontEnd/global"
import { error } from "../utils"
import { checkShape } from "../FrontEnd/checkShape.js"
//对于未实现toJS函数的节点, 降级执行toString
Node.prototype.toJS = function(){
return this.toString()
}
Number.prototype.toJS = function(){ return this.toString() }
String.prototype.toJS = function(){ return this.toString() }
/**
* 输入一个 list 返回它转化后的 string, 可以配置分隔符 split 例如','
* 也可以配置 start 和 end 例如'{' '}'
*/
function list2String(list, split, start, end) {
if (!list || list.length == 0) return ''
var str = start ? start : ''
list.forEach((x, idx) => {
str += x.toJS()
str += split && idx < list.length - 1 ? split : ''
})
return end ? str + end : str
}
matrix_constant.prototype.toJS = function(){
if(this.shape[1] === 1){
return `nj.array([${this.rawData.join(',')}])`
}else {
return `nj.array([${this.rawData.map(arr => '['+arr.join(',')+']').join(',')}])`
}
}
declarator.prototype.toJS = function () {
var str = this.identifier.name
str += this.op ? this.op : ''
if(this.identifier.arg_list.length && !this.initializer){
str += `= getNDArray(${this.identifier.arg_list.map(_=>_.toJS()).join(',')})`
}else if (this.initializer instanceof Array) {
str += list2String(this.initializer, ',', '[', ']')
} else {
str += this.initializer ? this.initializer.toJS() : ''
}
return str
}
declareNode.prototype.toJS = function () {
if(this.type === 'Matrix'){
var res = ''
for(let decla of this.init_declarator_list){
if(Array.isArray(decla.initializer)){
const name = decla.identifier.name
res += `let ${name} = [] \n`
for(let i=0; i< decla.initializer.length;i++){
res += `${name}[${i}] = ${decla.initializer[i].toJS()} \n`
}
}else{
res += `let ${decla.identifier.name} = ${decla.initializer.toJS()} \n`
}
}
return res
}else{
return 'let ' + list2String(this.init_declarator_list, ', ')
}
}
idNode.prototype.toJS = function(){
return this.name.toJS() + (this.arg_list.length > 0? list2String(this.arg_list, '][','[',']') :'').replace(/\[0]/g,'[]')
}
//将每一行 statement 的';'上提至 blockNode 处理
blockNode.prototype.toJS = function () {
if (!this.stmt_list || this.stmt_list.length == 0) return '{ }'
var str = '{\n';
this.stmt_list.forEach(x => {
str += x.toJS() + '\n'
})
return str + '}\n'
}
function getNdConstantMatrix(shape, number){
return `nj.zeros([${shape[0]},${shape[1]}]).assign(${number})`
}
function resolveNumber(node, shape, mainShape){
if(shape.join() !== mainShape.join()){
return `nj.constant(${mainShape[0]},${mainShape[1]},${node.toJS()})`
}else{
return node.toJS()
}
}
//expNode 的子类
binopNode.prototype.toJS = function () {
if(COStreamJS.plugins.matrix){
if('=' === this.op && this.left instanceof matrix_section && this.left.slice_pair_list.length > 1){
return matrixAssignmentToJS(this.left, this.right)
}
const lshape = top.shapeCache.get(this.left), rshape = top.shapeCache.get(this.right)
if(lshape && lshape != "1,1" || rshape && rshape != "1,1"){
if(['+','-','*','/'].includes(this.op)){
if(this.op === '*' && lshape != "1,1" && rshape != "1,1"){
return this.left.toJS()+'.dot('+this.right.toJS()+')' // 矩阵乘法
}
const mainShape = lshape != "1,1" ? lshape : rshape
const lString = resolveNumber(this.left,lshape,mainShape)
const rString = resolveNumber(this.right, rshape, mainShape)
const handlers = {
'+': 'add',
'-': 'subtract',
'*': 'dot',
'/': 'divide'
}
return lString + `.` + handlers[this.op] + `(${rString})`
}else if(['+=','-=','*=','/='].includes(this.op)){
const rightNode = new binopNode(null,this.left,this.op[0],this.right)
const assignNode = new binopNode(null, this.left,'=', rightNode)
return assignNode.toJS()
}
}
}
if(this.op === '.'){
return this.left.toJS() + this.op + this.right.toString() // 点操作符的右侧不需要加this
}
return this.left.toJS() + this.op + this.right.toJS()
}
function matrixAssignmentToJS(/** @type {matrix_section}*/left, right){
if(left.slice_pair_list.some(x => x.op)){
throw new Error(error(left._loc,"WEB端代码生成左侧暂不支持使用冒号:"))
}
const list = left.slice_pair_list.map(s => s.start)
return `${left.exp.toJS()}.set(${list[0]},${list[1]}, ${right.toJS()})`
}
callNode.prototype.toJS = function(){
if(this.name instanceof binopNode){
if(this.name.right === 'cwiseProduct'){
return this.name.left.toJS() + '.multiply(' + list2String(this.arg_list,',') + ')'
}else{
return this.name.left.toJS() + `.${this.name.right}(` + list2String(this.arg_list,',') + ')'
}
}else if(this.name instanceof lib_binopNode){
if(this.name.function_name === 'constant'){
return getNdConstantMatrix(this.arg_list, this.arg_list[2])
}
return `nj.${this.name.function_name}([${this.arg_list}])`
}else{
if (this.name === "print") {
return 'console.log(' + list2String(this.arg_list, ",") + ')'
}else if (this.name === "println") {
return 'console.log(' + list2String(this.arg_list, ',') + `,'\\n')`
}else if (BUILTIN_MATH.includes(this.name)){
return 'Math.'+this.name + '(' + list2String(this.arg_list, ',') + ')'
}else if(this.name === "Native"){
if(this.arg_list[1].source.slice(1,-1) !== 'WEB'){
throw new Error(error(this._loc, `该 Native 函数与当前的执行平台不符`))
}
return this.arg_list[0].source.slice(1,-1) //通过 slice 来移除左右两侧的引号
}else{
return this.name.toJS() + '(' + list2String(this.arg_list, ',') + ')'
}
}
}
forNode.prototype.toJS = function () {
var str = 'for('
str += this.init ? this.init.toJS() + ';' : ';'
str += this.cond ? this.cond.toJS() + ';' : ';'
str += this.next ? this.next.toJS() : ''
str += ')' + this.statement.toJS()
str += this.statement instanceof blockNode ? '' : ';' // 若该 for 只有一条语句, 则补充一个分号
return str
}
matrix_section.prototype.toJS = function(){
const shape = top.shapeCache.get(this.exp)
if(shape && shape.length >= 3 && this.slice_pair_list.length === 1){ //matrixs是矩阵数组,访问matrixs[0]的情况
return `${this.exp.toJS()}[${this.slice_pair_list[0].start.toJS()}]`
}
if(shape && shape.join('') > '11' || this.slice_pair_list.length > 1){
// 若为S.x[i,j]获取指定位置元素
if(this.slice_pair_list.every(p => p.op !== ':')){
const indexs = this.slice_pair_list.map(p=>p.start)
return `${this.exp.toJS()}.get(${indexs[0]},${indexs[1]})`
}
// 若为S.x[i:i+m,j:j+n]切片
const args = this.slice_pair_list.map(getNumJSSliceString)
return `${this.exp.toJS()}.slice(${args})`
}else{
// 不是矩阵的情况
return this.exp.toJS() + '[' + list2String(this.slice_pair_list, ',') + ']'
}
}
// 根据numjs库的复杂切片规则生成其切片字符串
function getNumJSSliceString(/** @type {{ start?: string, op?: ':', end?: string}} */ pair){
const { start, op, end } = pair
if(!op){
return `[${start},${start}+1]`
}else{
if(!start && end){
return `[${end}]`
}else if(start && !end){
return `${start}`
}else if(start && end){
return `[${start},${end}]`
}else if(!start && !end){
return `0`
}
}
}
// 对string进行特殊处理, 必要时在前方添加this
String.prototype.toJS = function toJS(){
// 对string进行特殊处理, 必要时在前方添加this
if(!/[A-z_][A-z0-9_]*/.test(this)) return this // 若不是标识符则不处理
let searchResult = top.searchName(this)
if(top.paramNames.includes(this)){
return 'this.'+this
}else if(searchResult){
// 替换符号表中的成员变量和流变量的访问
if(searchResult.type === 'stream' || searchResult.type === 'member'){
return 'this.'+this
}else if(searchResult.type === 'variable'){
// 如果该变量是属于根符号表中的全局变量
if(searchResult.origin === top.root){
return this
}
// 如果该变量名是 composite 中定义的过程变量, 则替换 oper 对上层符号表的数据的访问
else if(searchResult.origin !== top){
return top.getVariableValue(this)
}
}
}
return this
}
ternaryNode.prototype.toJS = function (){
return this.first.toJS() + '?' + this.second.toJS() + ':' + this.third.toJS();
}
castNode.prototype.toJS = function () {
if(this.type === 'int') return `Math.floor(${this.exp.toJS()})`
else return this.exp.toJS()
}
parenNode.prototype.toJS = function () {
return '(' + this.exp.toJS() + ')'
}
unaryNode.prototype.toJS = function () {
if(this.first === '+' || this.first === '-'){
const shape = top.shapeCache.get(this)
if(shape && shape.join('') !== '11'){
const oper = this.first === '+' ? 'add' : 'subtract'
return `nj.constant(${shape[0]},${shape[1]},0).${oper}(${this.second.toJS()})`
}
}
return '' + this.first.toJS() + this.second.toJS()
}
whileNode.prototype.toJS = function (){
return 'while(' + this.exp.toJS() + ')' + this.statement.toJS();
}
doNode.prototype.toString = function (){
return 'do' + this.statement.toJS() + 'while(' + this.exp.toJS() + ')'
}
selection_statement.prototype.toJS = function () {
if (this.op1 === 'if') {
var str = 'if(' + this.exp.toJS() + ')' + this.statement.toJS()
str += this.op4 === 'else' ? ('else ' + this.else_statement.toJS()) : ''
return str
} else if (this.op1 == 'switch') {
}
}
matrix_slice_pair.prototype.toJS = function (){
if(!this.op) return this.start.toJS()
return (this.start || '').toJS()+':'+(this.end||'').toJS()
}