Radicalc  Artifact [638200b5ec]

Artifact 638200b5ec9283d5e33b387c56458c81f9c3cbbf0f717113ce9a176db6b80a21:

  • File src/radicalc.js — part of check-in [d26be5d653] at 2017-08-06 15:44:19 on branch trunk — temporary copy of parameters (user: athaudia size: 6207)

var grammar = require("./parser.js");
var nearley = require("nearley");
var fs = require("fs");
var llvm = require("./llvm.js");
const { exec } = require('child_process');

function error(str) {
	console.log("error: " + str);
	process.exit(1);
}

var p = new nearley.Parser(grammar.ParserRules, grammar.ParserStart);

var src = fs.readFileSync("test.rcs", "utf8");
p.feed(fs.readFileSync("intrinsics.rcs", "utf8"))
p.feed(fs.readFileSync("win32.rcs", "utf8"));
p.feed(src);

if(p.results == 0) {
	console.log("no parse found");
	process.exit(1);
}
else if(p.results > 1) {
	console.log("ambiguity found");
	process.exit(1);
}

var tree = p.results[0];
var funs = [];
var dataTypes = {
	void:{name:"void",size:0},bool:{name:"bool",size:1},
	i8: {name:"i8", size:1},i16:{name:"i16",size:2},
	i32:{name:"i32",size:4},i64:{name:"i64",size:8},
	u8: {name:"u8", size:1},u16:{name:"u16",size:2},
	u32:{name:"u32",size:4},u64:{name:"u64",size:8}
};

function getDataType(str) {
	if(dataTypes[str] == null) {
		console.log("error: Data type '" + str + "' doesn't exist");
		process.exit(-1);
	}
	return dataTypes[str];
}

function getFun(name, paramTypes) {
	for(var j = 0; j < funs.length; ++j) {
		var fun = funs[j];
		if(fun.name == name && fun.params.length == paramTypes.length) {
			var pe = true;
			for(var i = 0; i < fun.params.length; ++i)
				pe = pe && (fun.params[i].dataType == paramTypes[i]);
			if(pe)
				return fun;
		}
	}
	error("can't find function '"+ name + "'");
}

function preAnnotate() {
	tree.funs.forEach(function(fun) {
		fun.params = fun.params || [];
		fun.params.forEach(function(param) {
			param.dataType = getDataType(param.dataTypeStr);
			delete param.dataTypeStr;
		});
		fun.dataType = getDataType(fun.dataTypeStr);
		delete fun.dataTypeStr;
		funs.push(fun);
	});
}

function getVar(scope, name) {
	while(scope) {
		if(scope.vars[name])
			return scope.vars[name];
		scope = scope.parent;
	}
	error("can't find variable '"+name+"'");
}

function annotate(node) {
	switch(node.type) {
		case "assign":
			annotate(node.expr);
			node.var = getVar(node.scope, node.name);
			node.dataType = node.expr.dataType;
			if(node.dataType != getVar(node.scope, node.name).dataType)
				error("trying to assign wrong data type");
			break;
		case "funCall":
			node.params.forEach(function(param){
				annotate(param);
			});
			node.funCall = getFun(node.name, node.params.map(function(param) {
				return param.dataType;
			}));
			node.dataType = node.funCall.dataType;
			break;
		case "litInt":
			node.dataType = dataTypes.i32;
			break;
		case "return":
			if(node.expr == null) {
				if(node.fun.dataType.name != "void")
					error("can't void return in function with return type");
			}
			else {
				annotate(node.expr);
				node.dataType = node.expr.dataType;
				if(node.dataType != node.fun.dataType)
					error("wrong datatype in return");
			}
			break;
		case "stmtExpr":
			annotate(node.expr);
			break;
		case "varDef":
			if(node.dataTypeStr == null && node.expr == null)
				error("vardef of "+node.name+" has no datatype");
			if(node.expr)
				annotate(node.expr);
			if(node.dataTypeStr == null)
				node.dataType = node.expr.dataType;
			else
				node.dataType = getDataType(node.dataTypeStr);
			if(node.dataType == null)
				error("vardef has no datatype");
			break;
		case "varUse":
			node.var = getVar(node.scope, node.name);
			node.dataType = node.var.dataType;
			break;
		case "while":
			annotate(node.expr);
			annotateBlock(node.block);
			break;
		default:
			error("not yet annotating '"+node.type+"'")
	}
}

function annotateBlock(block) {
	block.statements.forEach(function(stmt) {
		annotate(stmt);
	});
}

function annotateFunctions() {
	tree.funs.forEach(function(fun) {
		if(!fun.isStub)
			annotateBlock(fun.block);
	});
}

function initScope(node, scope, fun) {
	node.scope = scope;
	node.fun = fun;
	if(node.type == "varDef") {
		if(scope.vars[node.name])
			error("variable '"+node.name+"'already exists");
		scope.vars[node.name] = node;
	}
	else {
		if(node.params)
			node.params.forEach(function(param){initScope(param, scope)});
		if(node.expr)
			initScope(node.expr, scope, fun);
		
		if(node.block)
			initBlockScope(node.block, scope, fun);
		else if(node.ifBlock) {
			initBlockScope(node.ifBlock, scope, fun);
			if(node.elseBlock)
				initBlockScope(node.elseBlock, scope, fun);
		}
	}
}

function initBlockScope(block, parent, fun) {
	block.scope = {vars: {}, parent: parent};
	block.statements.forEach(function(statement) {
		initScope(statement, block.scope, fun);
	});
}

function initFunctionScopes() {
	tree.funs.forEach(function(fun) {
		if(fun.isStub)
			return;
		initBlockScope(fun.block, null, fun);
		fun.params = fun.params || [];
		fun.params.forEach(function(param){
			if(fun.block.scope.vars[param.name])
				error("variable '"+param.name+"'already exists");
			fun.block.scope.vars[param.name] = param;
			param.isParam = true;
		});
	});
}

////////////////////////////////////////

initFunctionScopes();
preAnnotate();
annotateFunctions();

llvm.initIntrinsics(dataTypes);
var out = fs.createWriteStream("tmp.ll");
llvm.outFunctions(out, funs);
llvm.outFunctions(process.stdout, funs);
out.end();
//process.exit(0);

exec('opt -O3 tmp.ll -S -o tmp.s && llc tmp.s -o tmp.o -filetype=obj && lld-link tmp.o "C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.14393.0\\ucrt\\x64\\ucrt.lib" /entry:main__i32 /subsystem:console && test2.bat', (error, stdout, stderr) => {
  if (error) {
  	console.error(`exec error: ${error}`);
  	return;
  }
  console.log(stdout);
});

/* todo:
 - change temporaries to be stack allocated variables...
   let llvm optimiser handle that, and just write a single generic way of
   doing things; always copy before passing to function and delete after
 - copy params into temporary stack allocated variables
 - modulo operator
 - unsigned intrinsics
 - destructors... when to call them?
   - whenever assigning to variable, first destruct what's in it
   - temporaries... when?
     - end of current statement, or...
     - as soon as it's not needed anymore
 - difference between variables, llvm-temporaries and radicalc-temporaries?
 - copy before returning?
 - strings?
*/