feat(Target): Add fasm target

- Add x86_64-fasm-linux-gnu
- Update Makefile
- Update parser to support '\n'
This commit is contained in:
Gers2017 2023-05-04 17:35:57 -06:00
parent 605530e25a
commit bf94a1eacb
5 changed files with 74 additions and 12 deletions

4
.gitignore vendored
View file

@ -1,6 +1,8 @@
/bin /bin
/**/*.out
/ignore /ignore
/**/*.out
*.rs *.rs
*.asm
*.elf
test.cpp test.cpp
test.rs test.rs

View file

@ -1,14 +1,20 @@
CC=rustc CC=rustc
ASM=fasm
default: build-rs default: build-rs
build-rs: build-rs:
node main.js main.cpp && $(CC) main.rs -o main.out node main.js main.cpp --target rust && $(CC) main.rs -o main.out
build-fasm:
node main.js main.cpp --target x86_64-fasm-linux-gnu && $(ASM) main.asm main.elf && chmod +x main.elf
run: run:
./main.out ./main.out
run-fasm:
./main.elf
clean: clean:
rm *.out rm *.out

View file

@ -2,7 +2,7 @@ import { writeFileSync } from "fs";
import { Stmt, PrintStmt, ReturnStmt } from "./stuff.js"; import { Stmt, PrintStmt, ReturnStmt } from "./stuff.js";
/** /**
* * @param {string} filename
* @param {Stmt[]} statements * @param {Stmt[]} statements
*/ */
export function generate_rust_code(filename, statements) { export function generate_rust_code(filename, statements) {
@ -22,3 +22,49 @@ export function generate_rust_code(filename, statements) {
writeFileSync(filename, text); writeFileSync(filename, text);
} }
/**
* @param {string} filename
* @param {Stmt[]} statements
*/
export function generate_fasm_linux(filename, statements) {
const output = [];
output.push("format ELF executable 3");
output.push("entry start");
output.push("segment readable executable");
output.push("start:");
const push_syscall = () => output.push("int 0x80");
for (let i = 0; i < statements.length; i++) {
const stmt = statements[i];
if (stmt instanceof PrintStmt) {
output.push("mov eax, 4"); // write syscall
output.push("mov ebx, 1"); // stdout (fd)
output.push(`mov ecx, str${i}`);
output.push(`mov edx, ${stmt.string.length}`);
push_syscall();
} else if (stmt instanceof ReturnStmt) {
output.push("mov eax, 1"); // exit syscall
output.push(`mov ebx, ${stmt.value}`); // return code
push_syscall();
}
}
output.push("segment readable writeable");
for (let i = 0; i < statements.length; i++) {
const stmt = statements[i];
if (stmt instanceof PrintStmt) {
const message = [...stmt.string]
.map((ch) => ch.charCodeAt(0))
.join(",");
output.push(`str${i} db ${message}`);
}
}
const text = output.join("\n");
writeFileSync(filename, text);
}

View file

@ -1,6 +1,6 @@
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { Parser } from "./parser.js"; import { Parser } from "./parser.js";
import { generate_rust_code } from "./interpreter.js"; import { generate_fasm_linux, generate_rust_code } from "./interpreter.js";
import { import {
is_alpha, is_alpha,
@ -366,7 +366,8 @@ function _main() {
const lexer = new Lexer(source); const lexer = new Lexer(source);
const tokens = lexer.scan_tokens(); const tokens = lexer.scan_tokens();
const parser = new Parser(tokens); const is_asm = target == TARGETS[1];
const parser = new Parser(tokens, { true_newline: is_asm });
const statements = parser.parse(); const statements = parser.parse();
switch (target) { switch (target) {
@ -377,7 +378,7 @@ function _main() {
case TARGETS[1]: // x86_64-fasm-linux-gnu case TARGETS[1]: // x86_64-fasm-linux-gnu
filename = filename.replace(".cpp", ".asm"); filename = filename.replace(".cpp", ".asm");
// TODO GENERATE ASM! generate_fasm_linux(filename, statements);
default: default:
break; break;
} }

View file

@ -25,7 +25,8 @@ const Errors = {
export class Parser { export class Parser {
/** @param { Token[] } tokens */ /** @param { Token[] } tokens */
constructor(tokens) { /** @param { { true_newline: boolean } | undefined } options */
constructor(tokens, options) {
this.tokens = tokens; this.tokens = tokens;
this.index = 0; this.index = 0;
this.back_index = this.tokens.length - 1; this.back_index = this.tokens.length - 1;
@ -33,6 +34,10 @@ export class Parser {
* @type { Stmt[] } * @type { Stmt[] }
*/ */
this.statements = []; this.statements = [];
this.options = options ?? {
// By default use '\\n'. Useful for transpiling to rust
true_newline: false,
};
} }
is_empty() { is_empty() {
@ -135,13 +140,15 @@ export class Parser {
if (token.token_type === TokenType.PRINTF) { if (token.token_type === TokenType.PRINTF) {
this.expect(TokenType.PRINTF, Errors.PRINTF_ERR); // skip print this.expect(TokenType.PRINTF, Errors.PRINTF_ERR); // skip print
this.expect(TokenType.LEFT_PAREN, Errors.LEFT_PAREN_ERR); // skip '(' this.expect(TokenType.LEFT_PAREN, Errors.LEFT_PAREN_ERR); // skip '('
const value = this.expect( let value = this.expect(TokenType.STRING, Errors.STRING_ERR).value; // get the string!
TokenType.STRING,
Errors.STRING_ERR
).value; // get the string!
this.expect(TokenType.RIGHT_PAREN, Errors.RIGHT_PAREN_ERR); // skip ')' this.expect(TokenType.RIGHT_PAREN, Errors.RIGHT_PAREN_ERR); // skip ')'
this.expect(TokenType.SEMICOLON, Errors.SEMI_COLON_ERR); this.expect(TokenType.SEMICOLON, Errors.SEMI_COLON_ERR);
if (this.options.true_newline) {
const regex = /\\n/gim;
value = value.replace(regex, "\n");
}
return new PrintStmt(value); return new PrintStmt(value);
} else if (token.token_type === TokenType.RETURN) { } else if (token.token_type === TokenType.RETURN) {
// console.log("current:", this.peek()?.display()); // console.log("current:", this.peek()?.display());