diff --git a/ast.js b/ast.js new file mode 100644 index 0000000..7e88815 --- /dev/null +++ b/ast.js @@ -0,0 +1,10 @@ +module.exports = { + Body(statements) { return { type: 'body', value: statements } }, + Link(identifier) { return { type: 'link', value: identifier } }, + Invocation(identifier, ...args) { return { type: 'invo', value: identifier, args } }, + Const(name, value) { return { type: 'const', value, name } }, + Int(n) { return { type: 'int', value: n } }, + String(s) { return { type: 'string', value: s } }, + Variable(name, value) { return { type: 'var', value, name } }, + VariableReference(name) { return { type: 'ref', value: name } } +} \ No newline at end of file diff --git a/compiler.js b/compiler.js new file mode 100644 index 0000000..7467b14 --- /dev/null +++ b/compiler.js @@ -0,0 +1,129 @@ + +const rname = () => (new Array(8).fill('')) + .map(() => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[ + Math.floor(Math.random() * 26) + ]).join(''); + +const linkables = { + log: require('./linkables/log.js'), + exit: require('./linkables/exit.js') +}; + +const callingConvention = { + SYSCALL_REGISTER: 'rax', + PARAMETER_REGISTERS: ['rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9'] +} + +const staticVariables = new Map(); +const linkedLibraries = new Map(); +const statements = []; +// this maybe should be absctracted into scopes but /shrug +const localVariables = new Map(); + +const sections = { + data() { + const convert = ([name, xs]) => name + ' db ' + xs.join(',') + return 'section .data\n ' + + [...staticVariables.entries()] + .map(convert) + .join('\n ') + }, + text() { + return 'section .text\n global _start\n_start:\n push rbp\n mov rbp, rsp\n ' + + statements.join('\n ') + + '\n mov rsp, rbp\n pop rbp\n mov rax, 60\n mov rdi, 0\n syscall\n' + + [...linkedLibraries.values()].map(({asmName, asm}) => asmName + ':\n' + asm).join('\n') + } +} + +function zip(a, b) { + const len = Math.min(a.length, b.length); + const ret = []; + for(let i = 0; i < len; i ++) { + ret.push([a[i], b[i]]); + } + return ret; +} + +function compileStringLiteral(str) { + const varName = rname(); + staticVariables.set(varName, Buffer.from(str + '\x00')); + return varName; +} + +function compileRef(name) { + console.assert(localVariables.has(name), 'unknown variable ' + name); + return `[rbp - ${localVariables.get(name).offset}]`; +} + +function compileInvocation(name, ...args) { + console.assert(linkedLibraries.has(name), 'unknown function ' + name); + console.assert(args.length <= 6, 'functions cannot have more than 6 arguments'); + const argMoves = []; + for(const [register, argument] of zip(callingConvention.PARAMETER_REGISTERS, args)) { + if(typeof argument === 'object') { + switch (argument.type) { + case 'string': { + argMoves.push(`mov ${register}, ${compileStringLiteral(argument.value)}`) + break; + } + case 'ref': { + const reference = compileRef(argument.value); + argMoves.push(`mov ${register}, ${reference}`) + break; + } + default: { + console.error('unhandled argument type ' + argument.type); + } + } + } + } + statements.push(...argMoves); + statements.push('call ' + linkedLibraries.get(name).asmName); +} + +function compileVariable(name, value) { + const typesize = 8; + console.assert(!localVariables.has(name), 'duplicate variable name ' + name); + localVariables.set(name, { + // !! this is wrong. \/ not everything is a 64 bit # + offset: typesize + localVariables.size * typesize, + size: typesize + }); + if(value.type === 'string') { + const variableName = compileStringLiteral(value.value); + statements.push('push ' + variableName) + } else { + console.error('dont know how to set a variable to a non string lol') + } +} + +function compileStatement(item) { + switch(item.type) { + case 'link': { + console.assert(item.value in linkables, 'Cannot find linked module: ' + item.value); + linkedLibraries.set(item.value, linkables[item.value]); + break; + } + case 'invo': { + compileInvocation(item.value, ...item.args); + break; + } + case 'var': { + compileVariable(item.name, item.value); + break; + } + default: { + console.error('unhandled statement ' + item.type); + } + } +} + +function compile(tree) { + for(const item of tree.value) { + compileStatement(item); + } + return sections.data() + '\n' + sections.text(); +} + +module.exports = compile; \ No newline at end of file diff --git a/disco.asm b/disco.asm index bae15c7..af5987e 100644 --- a/disco.asm +++ b/disco.asm @@ -1,7 +1,7 @@ section .data - text db "abc",10,0 + section .bss stackArguments resb 64 diff --git a/disco.disco b/disco.disco index 1372c42..990d1f0 100644 --- a/disco.disco +++ b/disco.disco @@ -1,3 +1,4 @@ link log -log("hello\n") \ No newline at end of file +log("Hello") +log("World") \ No newline at end of file diff --git a/disco.js b/disco.js index da29ca5..46e0775 100755 --- a/disco.js +++ b/disco.js @@ -1,140 +1,26 @@ #!/usr/bin/env node -const AST = { - Body(statements) { return { type: 'body', value: statements } }, - Identifier(name) { return { type: 'iden', value: name } }, - Link(identifier) { return { type: 'link', value: identifier } }, - Const(name, value) { return { type: 'const', name, value } }, - Int(n) { return { type: 'int', value: n } }, - String(s) { return { type: 'string', value: s } }, - Invocation(identifier, ...args) { return { type: 'invo', value: identifier, args } } -} - -const linkables = { - log: { - asmName: '_log', - asm: `\ - push rax - mov rbx, 0 -_log_loop: - mov cl, [rax] - cmp cl, 0 - je _log_loop_end - inc rax - inc rbx - jmp _log_loop -_log_loop_end: - mov rdx, rbx - mov rax, 1 - mov rdi, 1 - pop rsi - syscall - ret` - } -} - -function compile(body) { - const linkedFunctions = {} - - // add linked functions - // add static literals - // add main statments - const rname = () => (new Array(8).fill('')).map(() => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[Math.floor(Math.random() * 26)]).join(''); - const literals = { - - } - let mainStatements = ''; - - const parseLink = (identifier) => { - console.assert(identifier.type === 'iden', 'EXPECTED IDENTIFIER AFTER LINK'); - console.assert(identifier.value in linkables, 'CANNOT FIND LINK ' + identifier.value); - linkedFunctions[identifier.value] = linkables[identifier.value]; - } - - const parseString = (string) => { - const name = rname(); - literals[name] = string.value - return name; - } - - const parseInvocation = (invocation) => { - console.assert(invocation.value.value in linkedFunctions, 'UNKNOWN FUNCTION ' + invocation.value); - - const stuffIdk = []; - - for(const arg of invocation.args) { - switch(arg.type) { - case 'string': { - const asmName = parseString(arg); - stuffIdk.push(asmName); - break; - } - } - } - - mainStatements += `\ - mov rax, ${stuffIdk[0]} - call ${linkedFunctions[invocation.value.value].asmName} -` - - } - - const parseBody = (body) => { - for(const node of body.value) { - switch(node.type) { - case 'link': { - parseLink(node.value); - break; - } - case 'invo': { - parseInvocation(node); - break; - } - } - } - } - - parseBody(body); - - return `\ -section .data -${Object.entries(literals).map(([name, string]) => { - return " " + name + " db \"" + string + "\",10,0"; -})} -section .text - global _start - -_start: -${mainStatements} - call _exit - -_exit: - mov rax, 60 - mov rdi, 0 - syscall - -${Object.values(linkedFunctions).map(({asmName, asm}) => { - return `${asmName}:\n${asm}` -}).join('\n\n')}` -} - - -require('fs').writeFileSync('out.asm', compile(AST.Body([ - AST.Link(AST.Identifier('log')), - AST.Invocation( - AST.Identifier('log'), - AST.String('Hello World') - ) -]))); +const AST = require('./ast.js'); +const compile = require('./compiler.js'); +const myProgram = AST.Body([ + AST.Variable('test1', AST.String('This String is Contained in a variable')), + AST.Variable('test2', AST.String('This is a second string in a variable')), + AST.Link('log'), + AST.Invocation('log', AST.String('hello')), + AST.Invocation('log', AST.String('world')), + AST.Invocation('log', AST.VariableReference('test1')), + AST.Invocation('log', AST.VariableReference('test2')), + AST.Invocation('log', AST.VariableReference('test1')), + AST.Invocation('log', AST.VariableReference('test2')), +]); +const asmFile = compile(myProgram) try { - require('child_process').execSync('nasm -f elf64 out.asm -o out.o'); - require('child_process').execSync('ld out.o -o out'); + require('fs').writeFileSync('out.asm', asmFile); + require('child_process').execSync('nasm -f elf64 out.asm -o out.o', { stdio: 'inherit' }); + require('child_process').execSync('ld out.o -o out', { stdio: 'inherit' }); require('child_process').execSync('./out', { stdio: 'inherit' }); } catch (e) { - + process.exit(1); } - - - diff --git a/linkables/exit.js b/linkables/exit.js new file mode 100644 index 0000000..9d602da --- /dev/null +++ b/linkables/exit.js @@ -0,0 +1,9 @@ +module.exports = { + asmName: '_exit_with_code', + asm: `\ + push rax + mov rax, 60 + pop rdi + syscall +` +} \ No newline at end of file diff --git a/linkables/log.js b/linkables/log.js new file mode 100644 index 0000000..cc14c24 --- /dev/null +++ b/linkables/log.js @@ -0,0 +1,27 @@ +module.exports = { + asmName: '_log', + asm: `\ + push rdi + mov rbx, 0 +_log_loop: + mov cl, [rdi] + cmp cl, 0 + je _log_loop_end + inc rdi + inc rbx + jmp _log_loop +_log_loop_end: + mov rdx, rbx + mov rax, 1 + mov rdi, 1 + pop rsi + syscall + push 10 + mov rax, 1 + mov rdi, 1 + mov rsi, rsp + mov rdx, 1 + syscall + pop rdi + ret` +} \ No newline at end of file diff --git a/out.asm b/out.asm index c5e317b..de74bc6 100644 --- a/out.asm +++ b/out.asm @@ -1,27 +1,40 @@ section .data - YMYJYNCD db "Hello World",10,0 + QVGWSIUM db 84,104,105,115,32,83,116,114,105,110,103,32,105,115,32,67,111,110,116,97,105,110,101,100,32,105,110,32,97,32,118,97,114,105,97,98,108,101,0 + ZYXGJUBF db 84,104,105,115,32,105,115,32,97,32,115,101,99,111,110,100,32,115,116,114,105,110,103,32,105,110,32,97,32,118,97,114,105,97,98,108,101,0 + GPBLFTCX db 104,101,108,108,111,0 + GXMDWCDF db 119,111,114,108,100,0 section .text global _start - _start: - mov rax, YMYJYNCD + push rbp + mov rbp, rsp + push QVGWSIUM + push ZYXGJUBF + mov rdi, GPBLFTCX call _log - - call _exit - -_exit: + mov rdi, GXMDWCDF + call _log + mov rdi, [rbp - 8] + call _log + mov rdi, [rbp - 16] + call _log + mov rdi, [rbp - 8] + call _log + mov rdi, [rbp - 16] + call _log + mov rsp, rbp + pop rbp mov rax, 60 mov rdi, 0 syscall - _log: - push rax + push rdi mov rbx, 0 _log_loop: - mov cl, [rax] + mov cl, [rdi] cmp cl, 0 je _log_loop_end - inc rax + inc rdi inc rbx jmp _log_loop _log_loop_end: @@ -30,4 +43,11 @@ _log_loop_end: mov rdi, 1 pop rsi syscall + push 10 + mov rax, 1 + mov rdi, 1 + mov rsi, rsp + mov rdx, 1 + syscall + pop rdi ret \ No newline at end of file