disco/disco.js

141 lines
3.0 KiB
JavaScript
Raw Normal View History

2022-02-22 03:39:07 -05:00
#!/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')
)
])));
try {
require('child_process').execSync('nasm -f elf64 out.asm -o out.o');
require('child_process').execSync('ld out.o -o out');
require('child_process').execSync('./out', { stdio: 'inherit' });
} catch (e) {
}