mirror of
https://github.com/Gers2017/cpp.js.git
synced 2024-11-22 14:58:42 -06:00
feat(Target): Add fasm target
- Add x86_64-fasm-linux-gnu - Update Makefile - Update parser to support '\n'
This commit is contained in:
parent
605530e25a
commit
bf94a1eacb
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
||||||
/bin
|
/bin
|
||||||
/**/*.out
|
|
||||||
/ignore
|
/ignore
|
||||||
|
/**/*.out
|
||||||
*.rs
|
*.rs
|
||||||
|
*.asm
|
||||||
|
*.elf
|
||||||
test.cpp
|
test.cpp
|
||||||
test.rs
|
test.rs
|
10
Makefile
10
Makefile
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
7
main.js
7
main.js
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
17
parser.js
17
parser.js
|
@ -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());
|
||||||
|
|
Loading…
Reference in a new issue