diff --git a/src/codeGenerator/codeGenerator.ts b/src/codeGenerator/codeGenerator.ts index 50691ef..bc1ecd9 100644 --- a/src/codeGenerator/codeGenerator.ts +++ b/src/codeGenerator/codeGenerator.ts @@ -1,6 +1,6 @@ -import { assertNotUndefined, deepCopy } from '../repository/repository' +import { assertNotUndefined } from '../repository/repository' import { CONTRACT } from '../typings/contractTypes' -import { AST, DECLARATION_TYPES, MEMORY_SLOT, SENTENCES } from '../typings/syntaxTypes' +import { AST, MEMORY_SLOT, SENTENCES } from '../typings/syntaxTypes' import optimizer from './assemblyProcessor/optimizer' import genCode from './astProcessor/genCode' @@ -17,89 +17,6 @@ type SETUPGENCODE_ARGS = { * @returns assembly source code */ export default function codeGenerator (Program: CONTRACT) { - // holds variables needed during compilation - Program.Context = { - registerInfo: [], - isTemp, - getNewRegister, - freeRegister, - latestLoopId: [], - jumpId: 0, - assemblyCode: '', - errors: '', - currFunctionIndex: -1, - currSourceLine: 0, - scopedRegisters: [], - getNewJumpID: function () { - // Any changes here, also change function auxvarsGetNewJumpID - this.jumpId++ - return this.jumpId.toString(36) - }, - getLatestLoopID: function () { - // error check must be in code! - return this.latestLoopId[this.latestLoopId.length - 1] - }, - getLatestPureLoopID: function () { - // error check must be in code! - return this.latestLoopId.reduce((previous, current) => { - if (current.includes('loop')) { - return current - } - return previous - }, '') - }, - printFreeRegisters () { - let registers = 'r0' - for (let i = 1; i < Program.Config.maxAuxVars; i++) { - if (this.scopedRegisters.findIndex(item => item === `r${i}`) === -1) { - registers += `,r${i}` - } - } - this.assemblyCode += `^comment scope ${registers}\n` - }, - startScope: function (scopeName: string) { - this.scopedRegisters.push(scopeName) - if (Program.Config.verboseScope) { - this.printFreeRegisters() - } - }, - stopScope: function (scopeName: string) { - let liberationNeeded: string - do { - liberationNeeded = assertNotUndefined(this.scopedRegisters.pop(), 'Internal error') - if (/^r\d$/.test(liberationNeeded)) { - const motherMemory = assertNotUndefined(Program.memory.find(obj => - obj.asmName === liberationNeeded && - obj.type !== 'register' - ), 'Internal error') - motherMemory.address = -1 - motherMemory.asmName = '' - const Reg = assertNotUndefined(this.registerInfo.find(Item => Item.Template.asmName === liberationNeeded)) - Reg.inUse = false - Reg.endurance = 'Standard' - } - } while (liberationNeeded !== scopeName) - if (Program.Config.verboseScope) { - this.printFreeRegisters() - } - }, - getMemoryObjectByName, - getMemoryObjectByLocation, - SentenceContext: { - isDeclaration: '', - isLeftSideOfAssignment: false, - isConstSentence: false, - isRegisterSentence: false, - hasVoidArray: false, - postOperations: '', - getAndClearPostOperations: function () { - const ret = this.postOperations - this.postOperations = '' - return ret - } - } - } - // main function for bigastCompile method, only run once. function generateMain () { // add Config Info @@ -448,98 +365,6 @@ export default function codeGenerator (Program: CONTRACT) { } } - function getMemoryObjectByName ( - varName: string, line: string, varDeclaration: DECLARATION_TYPES = '' - ) : MEMORY_SLOT { - let MemFound: MEMORY_SLOT | undefined - if (Program.Context.currFunctionIndex >= 0) { // find function scope variable - MemFound = Program.memory.find(obj => { - return obj.name === varName && obj.scope === Program.functions[Program.Context.currFunctionIndex].name - }) - } - if (MemFound === undefined) { - // do a global scope search - MemFound = Program.memory.find(obj => obj.name === varName && obj.scope === '') - } - if (MemFound === undefined) { - throw new Error(`At line: ${line}. Using variable '${varName}' before declaration.`) - } - if (MemFound.toBeRegister && MemFound.asmName === '') { - throw new Error(`At line: ${line}. Using variable '${varName}' out of scope!`) - } - if (!MemFound.isSet) { - detectAndSetNotInitialized(MemFound, line, varDeclaration !== '') - } - if (varDeclaration !== '') { // we are in declarations sentence - MemFound.isDeclared = true - return deepCopy(MemFound) - } - return deepCopy(MemFound) - } - - function getMemoryObjectByLocation (loc: number|bigint|string, line: string): MEMORY_SLOT { - let addr:number - switch (typeof loc) { - case 'number': addr = loc; break - case 'string': addr = parseInt(loc, 16); break - default: addr = Number(loc) - } - const FoundMemory = Program.memory.find(obj => obj.address === addr) - if (FoundMemory === undefined) { - throw new Error(`At line: ${line}. No variable found at address '${addr}'.`) - } - if (!FoundMemory.isSet) { - detectAndSetNotInitialized(FoundMemory, line, false) - } - return deepCopy(FoundMemory) - } - - function detectAndSetNotInitialized (Memory: MEMORY_SLOT, line: string, isInitialization: boolean) { - if (Program.Context.SentenceContext.isLeftSideOfAssignment || Memory.hexContent) { - Memory.isSet = true - return - } - if (isInitialization) { - return - } - Program.warnings.push(`Warning: at line ${line}. Variable '${Memory.name}' is used but not initialized.`) - Memory.isSet = true // No more warning for same variable - } - - function isTemp (loc: number) : boolean { - if (loc === -1) return false - const id = Program.Context.registerInfo.find(OBJ => OBJ.Template.address === loc) - if (id === undefined) { - return false - } - if (Program.Context.scopedRegisters.find(items => items === id.Template.asmName)) { - // It is a register, but scoped. Do not mess!!! - return false - } - return true - } - - function getNewRegister (line: string): MEMORY_SLOT { - const id = Program.Context.registerInfo.find(OBJ => OBJ.inUse === false) - if (id === undefined) { - throw new Error(`At line: ${line}. ` + - 'No more registers available. ' + - `Increase the number with '#pragma maxAuxVars ${Program.Config.maxAuxVars + 1}' or try to reduce nested operations.`) - } - id.inUse = true - return deepCopy(id.Template) - } - - function freeRegister (loc: number|undefined): void { - if (loc === undefined || loc === -1) { - return - } - const RegInfo = Program.Context.registerInfo.find(OBJ => OBJ.Template.address === loc) - if (RegInfo === undefined) return - if (RegInfo.endurance === 'Scope') return - RegInfo.inUse = false - } - /** Run to start compilation in each sentence. */ function setupGenCode (CodeGenInfo: SETUPGENCODE_ARGS): string { CodeGenInfo.InitialAST = assertNotUndefined(CodeGenInfo.InitialAST) diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..671342c --- /dev/null +++ b/src/context.ts @@ -0,0 +1,171 @@ +import { GLOBAL_CONTEXT } from './codeGenerator/codeGeneratorTypes' +import { assertNotUndefined, deepCopy } from './repository/repository' +import { CONTRACT } from './typings/contractTypes' +import { DECLARATION_TYPES, MEMORY_SLOT } from './typings/syntaxTypes' + +export function createContext (Program: CONTRACT) : GLOBAL_CONTEXT { + function detectAndSetNotInitialized (Memory: MEMORY_SLOT, line: string, isInitialization: boolean) { + if (Program.Context.SentenceContext.isLeftSideOfAssignment || Memory.hexContent) { + Memory.isSet = true + return + } + if (isInitialization) { + return + } + Program.Context.warnings.push(`Warning: at line ${line}. Variable '${Memory.name}' is used but not initialized.`) + Memory.isSet = true // No more warning for same variable + } + + return { + registerInfo: [], + isTemp (loc: number) : boolean { + if (loc === -1) return false + const id = this.registerInfo.find(OBJ => OBJ.Template.address === loc) + if (id === undefined) { + return false + } + if (this.scopedRegisters.find(items => items === id.Template.asmName)) { + // It is a register, but scoped. Do not mess!!! + return false + } + return true + }, + getNewRegister (line: string): MEMORY_SLOT { + const id = this.registerInfo.find(OBJ => OBJ.inUse === false) + if (id === undefined) { + throw new Error(`At line: ${line}. ` + + 'No more registers available. ' + + `Increase the number with '#pragma maxAuxVars ${Program.Config.maxAuxVars + 1}' or try to reduce nested operations.`) + } + id.inUse = true + return deepCopy(id.Template) + }, + freeRegister (loc: number|undefined): void { + if (loc === undefined || loc === -1) { + return + } + const RegInfo = this.registerInfo.find(OBJ => OBJ.Template.address === loc) + if (RegInfo === undefined) return + if (RegInfo.endurance === 'Scope') return + RegInfo.inUse = false + }, + latestLoopId: [], + jumpId: 0, + assemblyCode: '', + warnings: [], + errors: '', + currFunctionIndex: -1, + currSourceLine: 0, + scopedRegisters: [], + getNewJumpID: function () { + // Any changes here, also change function auxvarsGetNewJumpID + this.jumpId++ + return this.jumpId.toString(36) + }, + getLatestLoopID: function () { + // error check must be in code! + return this.latestLoopId[this.latestLoopId.length - 1] + }, + getLatestPureLoopID: function () { + // error check must be in code! + return this.latestLoopId.reduce((previous, current) => { + if (current.includes('loop')) { + return current + } + return previous + }, '') + }, + printFreeRegisters () { + let registers = 'r0' + for (let i = 1; i < Program.Config.maxAuxVars; i++) { + if (this.scopedRegisters.findIndex(item => item === `r${i}`) === -1) { + registers += `,r${i}` + } + } + this.assemblyCode += `^comment scope ${registers}\n` + }, + startScope (scopeName: string) { + this.scopedRegisters.push(scopeName) + if (Program.Config.verboseScope) { + this.printFreeRegisters() + } + }, + stopScope: function (scopeName: string) { + let liberationNeeded: string + do { + liberationNeeded = assertNotUndefined(this.scopedRegisters.pop(), 'Internal error') + if (/^r\d$/.test(liberationNeeded)) { + const motherMemory = assertNotUndefined(Program.memory.find(obj => + obj.asmName === liberationNeeded && + obj.type !== 'register' + ), 'Internal error') + motherMemory.address = -1 + motherMemory.asmName = '' + const Reg = assertNotUndefined(this.registerInfo.find(Item => Item.Template.asmName === liberationNeeded)) + Reg.inUse = false + Reg.endurance = 'Standard' + } + } while (liberationNeeded !== scopeName) + if (Program.Config.verboseScope) { + this.printFreeRegisters() + } + }, + getMemoryObjectByName ( + varName: string, line: string, varDeclaration: DECLARATION_TYPES = '' + ) : MEMORY_SLOT { + let MemFound: MEMORY_SLOT | undefined + if (this.currFunctionIndex >= 0) { // find function scope variable + MemFound = Program.memory.find(obj => { + return obj.name === varName && obj.scope === Program.functions[this.currFunctionIndex].name + }) + } + if (MemFound === undefined) { + // do a global scope search + MemFound = Program.memory.find(obj => obj.name === varName && obj.scope === '') + } + if (MemFound === undefined) { + throw new Error(`At line: ${line}. Using variable '${varName}' before declaration.`) + } + if (MemFound.toBeRegister && MemFound.asmName === '') { + throw new Error(`At line: ${line}. Using variable '${varName}' out of scope!`) + } + if (!MemFound.isSet) { + detectAndSetNotInitialized(MemFound, line, varDeclaration !== '') + } + if (varDeclaration !== '') { // we are in declarations sentence + MemFound.isDeclared = true + return deepCopy(MemFound) + } + return deepCopy(MemFound) + }, + getMemoryObjectByLocation (loc: number|bigint|string, line: string): MEMORY_SLOT { + let addr:number + switch (typeof loc) { + case 'number': addr = loc; break + case 'string': addr = parseInt(loc, 16); break + default: addr = Number(loc) + } + const FoundMemory = Program.memory.find(obj => obj.address === addr) + if (FoundMemory === undefined) { + throw new Error(`At line: ${line}. No variable found at address '${addr}'.`) + } + if (!FoundMemory.isSet) { + detectAndSetNotInitialized(FoundMemory, line, false) + } + return deepCopy(FoundMemory) + }, + SentenceContext: { + isDeclaration: '', + isLeftSideOfAssignment: false, + isConstSentence: false, + isRegisterSentence: false, + hasVoidArray: false, + postOperations: '', + getAndClearPostOperations: function () { + const ret = this.postOperations + this.postOperations = '' + return ret + } + } + } +} diff --git a/src/smartc.ts b/src/smartc.ts index a27fc5a..ca8a52f 100644 --- a/src/smartc.ts +++ b/src/smartc.ts @@ -8,6 +8,7 @@ import assembler from './assembler/assembler' import { PRE_TOKEN, TOKEN } from './typings/syntaxTypes' import { CONTRACT, MACHINE_OBJECT } from './typings/contractTypes' +import { createContext } from './context' /** * SmartC Compiler class. @@ -38,6 +39,7 @@ export class SmartC { private readonly sourceCode private preAssemblyCode?: string private MachineCode?: MACHINE_OBJECT + // @ts-ignore private Program: CONTRACT = { sourceLines: [], Global: { @@ -75,6 +77,7 @@ export class SmartC { this.Program.sourceLines = Options.sourceCode.split('\n') this.language = Options.language this.sourceCode = Options.sourceCode + this.Program.Context = createContext(this.Program) } /**