Compare commits

..

16 Commits

Author SHA1 Message Date
SpookyDervish
0e37cccd7a fixing a couple bugs 2025-09-14 05:37:47 +10:00
SpookyDervish
f561a849c2 fix of escape sequences 2025-09-13 19:29:22 +10:00
SpookyDervish
9e2846962c added not instruction and escape sequences 2025-09-13 19:29:06 +10:00
SpookyDervish
cc82d27b03 stdin works i think 2025-09-13 15:48:41 +10:00
SpookyDervish
983e1e8a74 added some command line args 2025-09-13 07:17:32 +10:00
SpookyDervish
86d59a8177 fixing quite a few bugs 2025-09-13 07:08:11 +10:00
SpookyDervish
170272353c functions basically work now, but they're extremely inneficient 2025-09-13 06:26:15 +10:00
SpookyDervish
865a31827a ok damn it nearly works 2025-09-12 06:28:45 +10:00
SpookyDervish
2ce71e7abb THE ADD FUNCTION COMPILES!!!!! 2025-09-10 20:45:22 +10:00
SpookyDervish
a23bcf7823 IT WORKSSSS, WE'RE ACCESSING THE ARGS!!!!! 2025-09-10 20:25:19 +10:00
SpookyDervish
523ccecbc0 RETURNING WORKSSSSS 2025-09-10 18:56:45 +10:00
SpookyDervish
3103d17026 WE CAN RETURN VALUES FROM FUNCTIONS LESS GOOO 2025-09-10 07:54:11 +10:00
SpookyDervish
59bef834c4 VERY basic functions implementation 2025-09-10 07:30:58 +10:00
SpookyDervish
e137cb88ae 39% speed increase from optimizer! 2025-09-09 18:03:58 +10:00
SpookyDervish
ab207d982c disabled optimizer prints 2025-09-09 17:48:23 +10:00
SpookyDervish
c2bdf555e4 optimizer fixed 👍 2025-09-09 17:47:43 +10:00
15 changed files with 679 additions and 348 deletions

BIN
a.out

Binary file not shown.

View File

@@ -23,3 +23,22 @@ def traceback(code: str, error_type: str, error_message: str, line: Union[int, N
exit(1)
def warning(code: str, warning_msg: str, note: str = None, line: Union[int, None] = None, start_column: Union[int, None] = None, end_column: Union[int, None] = None):
if line != None:
console.print(f"[bold cyan]warning:[/] {warning_msg}\n")
lines = code.split("\n")[line-1:line+2]
console.print(f"{line } > " + lines[0], highlight=False)
if start_column != None and end_column != None:
console.print(" " + (" " * start_column) + "[bold red]" + ("^" * (end_column-start_column+1)))
try:
console.print(f"{line+1} " + lines[1], highlight=False)
console.print(f"{line+2} " + lines[2], highlight=False)
console.print(" ...", highlight=False)
except IndexError: # the file is less than 3 lines i guess
pass
else:
console.print(f"[bold cyan]warning:[/] {warning_msg}")
if note != None:
console.print(f"[bold]note:[/] {note}")

View File

@@ -1,6 +1,25 @@
from ground_ast import RootNode
from typing import Any
from ground_ast import *
from error import warning
class SymbolTable:
def __init__(self, parent=None):
self.table = {} # variable name -> info (e.g., stack offset, type)
self.parent = parent
def define(self, name, value):
self.table[name] = value
def lookup(self, name):
scope = self
while scope:
if name in scope.table:
return scope.table[name]
scope = scope.parent
return None
#raise KeyError(f"Undefined variable: {name}")
class Generator:
@@ -9,19 +28,51 @@ class Generator:
self.lines: list[str] = []
self.code = code
self.output_path = output_path
self.variables = {}
self.global_scope = SymbolTable()
self.current_var_scope = self.global_scope
self.constants = {}
self.buffers = {}
self.structs = {}
self.functions: dict[str, dict] = {}
self.labels = []
self.arg_list = []
self.constants_reverse = {}
self.constant_counter = 0
self.buffer_counter = 0
self.included_functions = []
def ground_type_to_node(self, ground_type: str):
if ground_type == "string":
return StringNode
elif ground_type == "int":
return IntNode
elif ground_type == "float":
return FloatNode
elif ground_type == "bool":
return BoolNode
else:
return ground_type
def add_constant(self, value: Any, no_string: bool = False):
existing_constant_name = self.constants_reverse.get(value, None)
if existing_constant_name != None: return f"[.{existing_constant_name}]"
if existing_constant_name != None: return f"[{existing_constant_name}]"
self.constants["LC" + str(self.constant_counter)] = {"value": value, "no_string": no_string}
self.constants_reverse[value] = "LC" + str(self.constant_counter)
self.constant_counter += 1
return "[.LC" + str(self.constant_counter-1) + "]"
return "[LC" + str(self.constant_counter-1) + "]"
def add_buffer(self, size: int):
buffer_name = "BUF" + str(self.buffer_counter)
self.buffers[buffer_name] = size
self.buffer_counter += 1
return buffer_name
def add_function(self, node: FunctionNode):
self.functions[node.name] = {
"func": node,
"scope": SymbolTable(self.current_var_scope)
}
return self.functions[node.name]
def clamp_instruction_args(self, instruction: InstructionNode, min_args: int, max_args: int):
if len(instruction.arguments) < min_args:
@@ -37,11 +88,14 @@ class Generator:
node_type = str(type(node))[19:-2]
if not hasattr(self, f"generate_{node_type}"):
raise NotImplementedError(f"Generator has no generate method for {node_type}.")
getattr(self, f"generate_{node_type}")(node)
getattr(self, f"generate_{node_type}")(node, self.lines)
def generate(self):
for statement in self.ast.statements:
self.generate_node(statement)
for name, var in self.global_scope.table.items():
if not var["used"]:
warning(self.code, f"warning: \"{name}\" was defined but never used.")
def write(self):
with open(self.output_path + ".asm", "w") as f:

View File

@@ -1,12 +1,13 @@
from generators.generator import Generator
from generators.generator import Generator, SymbolTable
from ground_ast import *
from error import traceback
from error import traceback, warning
from optimizers.x86_64 import X86_64Optimizer
class X86_64Generator(Generator):
def __init__(self, ast, code, output_path):
super().__init__(ast, code, output_path)
self.stack_size = 0
self.function_lines = []
def init(self):
self.lines.append("global _start\n")
@@ -16,19 +17,37 @@ class X86_64Generator(Generator):
self.generate()
self.write()
def push(self, reg: str):
self.lines.append("push " + reg + "\n\t")
def push(self, reg: str, lines):
lines.append("push " + reg + "\n\t")
self.stack_size += 1
def pop(self, reg: str):
self.lines.append("pop " + reg + "\n\t")
def pop(self, reg: str, lines):
lines.append("pop " + reg + "\n\t")
self.stack_size -= 1
def get_variable(self, var_name: str, reg: str, float: bool = False, offset: int = 0, no_stack_pop: bool = True):
var = self.variables.get(var_name, None)
var_pos = self.get_var_pos(var_name)
def get_variable(self, lines, var_name: str, reg: str, float: bool = False, offset: int = 0, no_stack_pop: bool = True, scope: SymbolTable = None):
scope = scope or self.current_var_scope
var = scope.lookup(var_name)
var_pos = self.get_var_pos(var_name, scope)
if isinstance(var_pos, str): # in a reg, not on the stack
waste = {
"rax": "eax",
"rdi": "edi",
"rsi": "esi"
}
if waste[var_pos] == reg: # literally useless
return
if offset == 0:
lines.append(f"mov {reg}, {var_pos}\n\t")
elif offset == -8:
if var_pos == "rax": # according to the SysV ABI, the second return value of a function is stored in RDX
var_pos = "rdx"
lines.append(f"mov {reg}, {var_pos}\n\t")
else:
try:
#print(var["type"])
if var["type"] == FloatNode:
conversion = {
"rax": "xmm0",
@@ -37,59 +56,68 @@ class X86_64Generator(Generator):
# ...
}
self.lines.append(f"movsd {conversion[reg]}, [rsp + {var_pos + offset}]\n\t")
self.lines.append("add rsp, 8\n\t")
lines.append(f"movsd {conversion[reg]}, [rsp + {var_pos + offset}]\n\t")
lines.append("add rsp, 8\n\t")
self.stack_size += 1
elif var["type"] in [IntNode,StringNode]:
if no_stack_pop:
self.lines.append(f"mov {reg}, [rsp + {var_pos + offset}]\n\t")
lines.append(f"mov {reg}, [rsp + {var_pos + offset}]\n\t")
else:
self.push(
f"QWORD [rsp + {var_pos + offset}]"
f"QWORD [rsp + {var_pos + offset}]",
lines
)
self.pop(reg)
self.pop(reg, lines)
elif var["type"] == BoolNode:
if no_stack_pop:
self.lines.append(f"mov {reg}, [rsp + {var_pos + offset}]\n\t")
lines.append(f"mov {reg}, [rsp + {var_pos + offset}]\n\t")
else:
self.push(
f"QWORD [rsp + {var_pos + offset}]"
f"QWORD [rsp + {var_pos + offset}]",
lines
)
self.pop(reg)
self.pop(reg, lines)
except TypeError: # variable doesnt exist
except TypeError as e: # variable doesnt exist
traceback(self.code, "NameError", f"\"{var_name}\" is not defined.")
scope.table[var_name]["used"] = True
return var["type"]
def get_var_pos(self, var_name: str):
def get_var_pos(self, var_name: str, scope: SymbolTable = None):
scope = scope or self.current_var_scope
try:
return (self.stack_size - self.variables.get(var_name)['stack_loc'] - 1) * 8
except TypeError: # not defined
stack_loc = scope.lookup(var_name)['stack_loc']
if isinstance(stack_loc, str):
return stack_loc # its in a reg not on the stack
return (self.stack_size - stack_loc - 1) * 8
except TypeError as e: # not defined
traceback(self.code, "TypeError", f"\"{var_name}\" is not defined.")
def create_variable(self, var_name: str, starting_value, var_type: Any = None):
if var_type == None:
var_type = type(starting_value)
def create_variable(self, lines, var_name: str, starting_value, var_type: Any = None, scope = None):
var_type = var_type or type(starting_value)
scope = scope or self.current_var_scope
stack_location = self.stack_size
if type(starting_value) == IntNode:
self.lines.append(f"mov rax, {starting_value.value}\n\t")
self.push("rax")
lines.append(f"mov rax, {starting_value.value}\n\t")
self.push("rax", lines)
elif type(starting_value) == VarRefNode:
var_type = self.get_variable(starting_value.var_name, "rax")
var_type = self.get_variable(lines, starting_value.var_name, "rax", scope=scope)
if var_type == FloatNode:
self.lines.append("sub rsp, 8\n\t")
self.lines.append("movsd [rsp], xmm0\n\t")
lines.append("sub rsp, 8\n\t")
lines.append("movsd [rsp], xmm0\n\t")
else:
self.push("rax")
self.push("rax", lines)
elif type(starting_value) == FloatNode:
name = self.add_constant(starting_value.value)
self.lines.append("sub rsp, 8\n\t") # make space on the stack
self.lines.append(f"movsd xmm0, {name}\n\t")
self.lines.append("movsd [rsp], xmm0\n\t")
lines.append("sub rsp, 8\n\t") # make space on the stack
lines.append(f"movsd xmm0, {name}\n\t")
lines.append("movsd [rsp], xmm0\n\t")
self.stack_size += 1
elif type(starting_value) == StringNode:
@@ -97,83 +125,166 @@ class X86_64Generator(Generator):
starting_value.value
)
string_len = self.add_constant(f"equ $ - {string_pointer[1:-1]}", no_string=True)
self.lines.append(f"lea rax, {string_pointer}\n\t")
self.push("rax")
self.lines.append(f"mov rax, {string_len[1:-1]}\n\t")
self.push("rax")
lines.append(f"lea rax, {string_pointer}\n\t")
self.push("rax", lines)
lines.append(f"mov rax, {string_len[1:-1]}\n\t")
self.push("rax", lines)
elif type(starting_value) == BoolNode:
self.push("1" if starting_value.value else "0")
self.push("1" if starting_value.value else "0", lines)
elif type(starting_value) == str:
if starting_value.startswith("xmm"): # floating point stuff
self.lines.append("sub rsp, 8\n\t") # make space
self.lines.append(f"movsd [rsp], {starting_value}\n\t")
lines.append("sub rsp, 8\n\t") # make space
lines.append(f"movsd [rsp], {starting_value}\n\t")
self.stack_size += 1
else:
self.push(starting_value)
self.variables[var_name] = {"stack_loc": stack_location, "type": var_type}
if starting_value.startswith("BUF"):
self.push(starting_value, lines) # push buffer
self.push("rax", lines) # push length
else:
self.push(starting_value, lines)
scope.define(var_name, {"stack_loc": stack_location, "type": var_type, "used": False})
def change_variable(self, var_name: str, new_value):
var_pos = self.get_var_pos(var_name)
def change_variable(self, lines, var_name: str, new_value, scope: SymbolTable = None):
scope = scope or self.current_var_scope
var_pos = self.get_var_pos(var_name, scope)
old_var_type = scope.table[var_name]["type"]
if isinstance(var_pos, str):
if type(new_value) == IntNode: # we're changing a variable to a number
self.lines.append(f"mov QWORD [rsp + {var_pos}], {new_value.value}\n\t")
self.variables[var_name]["type"] = IntNode
#lines.append(f"mov QWORD [rsp + {var_pos}], {new_value.value}\n\t")
lines.append(f"mov {var_pos}, {new_value.value}\n\t")
scope.table[var_name]["type"] = IntNode
elif type(new_value) == VarRefNode: # we're changing a variable to the value of another variable
var_type = self.get_variable(new_value.var_name, "rax")
self.lines.append(f"mov QWORD [rsp + {var_pos}], rax\n\t")
self.variables[var_name]["type"] = var_type
var_type = self.get_variable(lines, new_value.var_name, "rax")
#lines.append(f"mov QWORD [rsp + {var_pos}], rax\n\t")
lines.append(f"mov {var_pos}, rax\n\t")
scope.table[var_name]["type"] = var_type
elif type(new_value) == StringNode: # we're changing a variable to a string
self.lines.append(f"mov QWORD [rsp + {var_pos}], 0\n\t")
lines.append(f"mov QWORD [rsp + {var_pos}], 0\n\t")
string_pointer = self.add_constant(new_value.value)
string_len = self.add_constant(f"equ $ - {string_pointer[1:-1]}", no_string=True)
#self.lines.append(f"lea QWORD [rsp + {var_pos}], {string_pointer}\n\t")
##self.lines.append(f"mov QWORD [rsp + {var_pos + 8}], {string_len[1:-1]}\n\t")
#lines.append(f"lea QWORD [rsp + {var_pos}], {string_pointer}\n\t")
##lines.append(f"mov QWORD [rsp + {var_pos + 8}], {string_len[1:-1]}\n\t")
self.variables[var_name]["stack_loc"] = self.stack_size
self.lines.append(f"lea rax, {string_pointer}\n\t")
self.push("rax")
self.lines.append(f"mov rax, {string_len[1:-1]}\n\t")
self.push("rax")
scope.table[var_name]["stack_loc"] = self.stack_size
lines.append(f"lea rax, {string_pointer}\n\t")
self.push("rax", lines)
lines.append(f"mov rax, {string_len[1:-1]}\n\t")
self.push("rax", lines)
self.variables[var_name]["type"] = StringNode
scope.table[var_name]["type"] = StringNode
elif type(new_value) == BoolNode:
self.lines.append(f"mov QWORD [rsp + {var_pos}], {"1" if new_value.value else "0"}\n\t")
self.variables[var_name]["type"] = BoolNode
#lines.append(f"mov QWORD [rsp + {var_pos}], {'1' if new_value.value else '0'}\n\t")
lines.append(f"mov {var_pos}, {int(new_value.value)}\n\t")
scope.table[var_name]["type"] = BoolNode
elif type(new_value) == str: # we're changing a variable to the value of a register
self.lines.append(f"mov QWORD [rsp + {var_pos}], {new_value}\n\t")
self.variables[var_name]["type"] = IntNode
#lines.append(f"mov QWORD [rsp + {var_pos}], {new_value}\n\t")
lines.append(f"mov {var_pos}, {new_value}\n\t")
#scope.table[var_name]["type"] = IntNode
def generate_LabelDecNode(self, node: LabelDecNode):
else:
if type(new_value) == IntNode: # we're changing a variable to a number
lines.append(f"mov QWORD [rsp + {var_pos}], {new_value.value}\n\t")
scope.table[var_name]["type"] = IntNode
elif type(new_value) == VarRefNode: # we're changing a variable to the value of another variable
var_type = self.get_variable(lines, new_value.var_name, "rax")
lines.append(f"mov QWORD [rsp + {var_pos}], rax\n\t")
scope.table[var_name]["type"] = var_type
elif type(new_value) == StringNode: # we're changing a variable to a string
lines.append(f"mov QWORD [rsp + {var_pos}], 0\n\t")
string_pointer = self.add_constant(new_value.value)
string_len = self.add_constant(f"equ $ - {string_pointer[1:-1]}", no_string=True)
#lines.append(f"lea QWORD [rsp + {var_pos}], {string_pointer}\n\t")
##lines.append(f"mov QWORD [rsp + {var_pos + 8}], {string_len[1:-1]}\n\t")
scope.table[var_name]["stack_loc"] = self.stack_size
lines.append(f"lea rax, {string_pointer}\n\t")
self.push("rax", lines)
lines.append(f"mov rax, {string_len[1:-1]}\n\t")
self.push("rax", lines)
scope.table[var_name]["type"] = StringNode
elif type(new_value) == BoolNode:
lines.append(f"mov QWORD [rsp + {var_pos}], {'1' if new_value.value else '0'}\n\t")
scope.table[var_name]["type"] = BoolNode
elif type(new_value) == str: # we're changing a variable to the value of a register
lines.append(f"mov QWORD [rsp + {var_pos}], {new_value}\n\t")
#scope.table[var_name]["type"] = IntNode
if scope.table[var_name]["type"] != old_var_type:
warning(self.code, f"Changing the type of \"{var_name}\" at runtime is considered bad practice.")
def generate_LabelDecNode(self, node: LabelDecNode, lines):
self.labels.append(node.name)
self.lines.append("." + node.name + ":\n\t")
lines.append("." + node.name + ":\n\t")
def generate_FunctionNode(self, node: FunctionNode, lines):
if node.return_type == None:
traceback(self.code, "SyntaxError", "Functions require a return type.")
if node.name == None:
traceback(self.code, "SyntaxError", "Functions require a name.")
func = self.add_function(node)
self.current_var_scope = func["scope"]
# function boilerplate
self.function_lines.append(node.name + ":\n\t")
self.push("rbp", self.function_lines)
self.function_lines.append("mov rbp, rsp\n\t")
for i, arg in enumerate(node.args):
try:
stack_loc = ["rdi","rsi","rdx","rcx","r8","r9"][i] # ""
except IndexError:
traceback(self.code, "CallError", "Functions with more than 6 args aren't supported yet, sorry...")
self.current_var_scope.define(arg.name, {"stack_loc": stack_loc, "type": self.ground_type_to_node(arg.arg_type), "used": False})
for inst in node.statements:
#self.function_lines.append(f"; {inst}\n\t")
node_type = str(type(inst))[19:-2]
getattr(self, f"generate_{node_type}")(inst, self.function_lines)
#self.generate_InstructionNode(inst, self.function_lines)
def generate_InstructionNode(self, node: InstructionNode, lines = None):
lines = lines or self.lines
def generate_InstructionNode(self, node: InstructionNode):
### MISC ###
if node.instruction == "end":
self.clamp_instruction_args(node, 1, 1)
if not type(node.arguments[0]) in [IntNode, VarRefNode]: # example: "end true"
traceback(self.code, "TypeError", f"end expects an integer, not {node.arguments[0]}")
self.lines.append("mov rax, 60\n\t")
if type(node.arguments[0]) in [IntNode,BoolNode]:
self.lines.append("mov rdi, " + str(node.arguments[0].value) + "\n\t")
lines.append("mov rdi, " + str(node.arguments[0].value) + "\n\t")
elif isinstance(node.arguments[0], VarRefNode):
var_type = self.get_variable(node.arguments[0].var_name, "rdi", no_stack_pop=True)
var_type = self.get_variable(lines, node.arguments[0].var_name, "rdi", no_stack_pop=True)
if var_type == FloatNode:
self.lines.append("cvttsd2si rdi, xmm0\n\t")
lines.append("cvttsd2si rdi, xmm0\n\t")
else:
if var_type not in [IntNode,BoolNode]:
traceback(self.code, "TypeError", f"end expects an integer, not \"{var_type}\"")
#self.lines.append("mov rdi, " + str(self.get_variable(node.arguments[0].var_name)) + "\n\t")
self.lines.append("syscall\n\t")
lines.append("mov rax, 60\n\t")
lines.append("syscall\n\t")
### VARIABLE INSTRUCTIONS ###
elif node.instruction == "set":
@@ -183,12 +294,12 @@ class X86_64Generator(Generator):
if type(node.arguments[1]) not in [IntNode, VarRefNode, FloatNode, StringNode, BoolNode]:
traceback(self.code, "TypeError", f"variables can't be of type \"{type(node.arguments[1])}\"")
variable_exists = self.variables.get(node.arguments[0].var_name, None) != None
variable_exists = self.current_var_scope.lookup(node.arguments[0].var_name) != None
if not variable_exists: # create a new variable
self.create_variable(node.arguments[0].var_name, node.arguments[1])
self.create_variable(lines, node.arguments[0].var_name, node.arguments[1])
else: # modify the existing one
self.change_variable(node.arguments[0].var_name, node.arguments[1])
self.change_variable(lines, node.arguments[0].var_name, node.arguments[1])
### MATH INSTRUCTIONS ###
elif node.instruction in ["add", "subtract", "multiply"]:
@@ -201,28 +312,28 @@ class X86_64Generator(Generator):
number2_type = None
arg2 = "rbx"
if isinstance(node.arguments[0], VarRefNode):
number1_type = self.get_variable(node.arguments[0].var_name, "rax")
number1_type = self.get_variable(lines, node.arguments[0].var_name, "rax")
elif isinstance(node.arguments[0], FloatNode) or isinstance(node.arguments[1], FloatNode):
number1_type = FloatNode
constant_name = self.add_constant(node.arguments[0].value)
self.lines.append(f"movsd xmm0, {constant_name}\n\t")
lines.append(f"movsd xmm0, {constant_name}\n\t")
elif isinstance(node.arguments[0], IntNode):
number1_type = IntNode
#arg1 = node.arguments[0].value
self.lines.append(f"mov rax, {node.arguments[0].value}\n\t")
lines.append(f"mov rax, {node.arguments[0].value}\n\t")
else:
traceback(self.code, "TypeError", f"expected a variable reference or number for argument 1 of add, got {node.arguments[0]}")
if isinstance(node.arguments[1], VarRefNode):
number2_type = self.get_variable(node.arguments[1].var_name, "rbx")
number2_type = self.get_variable(lines, node.arguments[1].var_name, "rbx")
elif number1_type == FloatNode or isinstance(node.arguments[1], FloatNode):
number2_type = FloatNode
constant_name = self.add_constant(node.arguments[1].value)
self.lines.append(f"movsd xmm1, {constant_name}\n\t")
lines.append(f"movsd xmm1, {constant_name}\n\t")
elif isinstance(node.arguments[1], IntNode):
number2_type = IntNode
arg2 = node.arguments[1].value
#self.lines.append(f"mov rbx, {node.arguments[1].value}\n\t")
#lines.append(f"mov rbx, {node.arguments[1].value}\n\t")
else:
traceback(self.code, "TypeError", f"expected a variable reference or number for argument 2 of add, got {node.arguments[1]}")
@@ -232,26 +343,26 @@ class X86_64Generator(Generator):
if number1_type == IntNode and number2_type == IntNode:
if node.instruction == "add":
self.lines.append(f"add rax, {arg2}\n\t")
lines.append(f"add rax, {arg2}\n\t")
elif node.instruction == "subtract":
self.lines.append(f"sub rax, {arg2}\n\t")
lines.append(f"sub rax, {arg2}\n\t")
elif node.instruction == "multiply":
self.lines.append(f"imul rax, {arg2}\n\t")
lines.append(f"imul rax, {arg2}\n\t")
else:
if node.instruction == "add":
self.lines.append(f"addsd xmm0, xmm1\n\t")
lines.append(f"addsd xmm0, xmm1\n\t")
elif node.instruction == "subtract":
self.lines.append(f"subsd xmm0, xmm1\n\t")
lines.append(f"subsd xmm0, xmm1\n\t")
elif node.instruction == "multiply":
self.lines.append(f"mulsd xmm0, xmm1\n\t")
lines.append(f"mulsd xmm0, xmm1\n\t")
is_integer = number1_type == IntNode and number2_type == IntNode
starting_reg = "rax" if is_integer else "xmm0"
if self.variables.get(node.arguments[2].var_name, None) == None: # we need to create a variable for the destination
self.create_variable(node.arguments[2].var_name, starting_reg, IntNode if is_integer else FloatNode)
if self.current_var_scope.lookup(node.arguments[2].var_name) == None: # we need to create a variable for the destination
self.create_variable(lines, node.arguments[2].var_name, starting_reg, IntNode if is_integer else FloatNode)
else:
self.change_variable(node.arguments[2].var_name, starting_reg)
self.change_variable(lines, node.arguments[2].var_name, starting_reg)
elif node.instruction == "divide":
self.clamp_instruction_args(node, 3, 3)
@@ -264,20 +375,20 @@ class X86_64Generator(Generator):
number2_type = None
if isinstance(node.arguments[0], VarRefNode):
number1_type = self.get_variable(node.arguments[0].var_name, "rax", True)
number1_type = self.get_variable(lines, node.arguments[0].var_name, "rax", True)
elif type(node.arguments[0]) in [IntNode, FloatNode]:
number1_type = FloatNode
constant_name = self.add_constant(node.arguments[0].value)
self.lines.append(f"movsd xmm0, {constant_name}\n\t")
lines.append(f"movsd xmm0, {constant_name}\n\t")
else:
traceback(self.code, "TypeError", f"expected a variable reference or number for argument 1 of divide, got {node.arguments[0]}")
if isinstance(node.arguments[1], VarRefNode):
number2_type = self.get_variable(node.arguments[1].var_name, "rbx", True)
number2_type = self.get_variable(lines, node.arguments[1].var_name, "rbx", True)
elif type(node.arguments[1]) in [IntNode, FloatNode]:
number2_type = FloatNode
constant_name = self.add_constant(node.arguments[1].value)
self.lines.append(f"movsd xmm1, {constant_name}\n\t")
lines.append(f"movsd xmm1, {constant_name}\n\t")
else:
traceback(self.code, "TypeError", f"expected a variable reference or number for argument 2 of divide, got {node.arguments[1]}")
@@ -285,12 +396,12 @@ class X86_64Generator(Generator):
if number1_type not in [IntNode, FloatNode] or number2_type not in [IntNode, FloatNode]:
traceback(self.code, "TypeError", f"Unsupported operation \"divide\" for \"{node.arguments[0]}\" and \"{node.arguments[1]}\".")
self.lines.append(f"divsd xmm0, xmm1\n\t")
lines.append(f"divsd xmm0, xmm1\n\t")
if self.variables.get(node.arguments[2].var_name, None) == None: # we need to create a variable for the destination
self.create_variable(node.arguments[2].var_name, "xmm0", FloatNode)
if self.current_var_scope.lookup(node.arguments[2].var_name) == None: # we need to create a variable for the destination
self.create_variable(lines, node.arguments[2].var_name, "xmm0", FloatNode)
else:
self.change_variable(node.arguments[2].var_name, "xmm0")
self.change_variable(lines, node.arguments[2].var_name, "xmm0")
elif node.instruction == "stdout":
self.clamp_instruction_args(node, 1, 1)
@@ -300,27 +411,49 @@ class X86_64Generator(Generator):
printed_value = arg.__str__()
if isinstance(arg, VarRefNode):
var_type = self.get_variable(arg.var_name, "rsi", offset=0, no_stack_pop=True)
var_type = self.get_variable(lines, arg.var_name, "rsi", offset=0, no_stack_pop=True)
if var_type == StringNode:
self.get_variable(arg.var_name, "rdx", offset=-8, no_stack_pop=True)
self.get_variable(lines, arg.var_name, "rdx", offset=-8, no_stack_pop=True)
else:
traceback(self.code, "TypeError", f"You can't print \"{var_type(None).__repr__()}\", try converting it to a string first.")
else:
string_pointer = self.add_constant(printed_value)[1:-1]
string_len = self.add_constant(f"equ $ - {string_pointer}", True)[1:-1]
self.lines.append(f"mov rsi, {string_pointer}\n\t")
self.lines.append(f"mov rdx, {string_len}\n\t") # length
lines.append(f"mov rsi, {string_pointer}\n\t")
lines.append(f"mov rdx, {string_len}\n\t") # length
self.lines.append("mov rax, 1\n\t") # sys_write syscall
self.lines.append("mov rdi, 1\n\t") # a file descriptor of 1 is stdout
self.lines.append("syscall\n\t")
self.push("rdi", lines) # save rdi
lines.append("mov rax, 1\n\t") # sys_write syscall
lines.append("mov rdi, 1\n\t") # a file descriptor of 1 is stdout
lines.append("syscall\n\t")
self.pop("rdi", lines) # restore rdi
elif node.instruction == "stdin":
self.clamp_instruction_args(node, 1, 1)
arg = node.arguments[0]
if not isinstance(arg, VarPointerNode):
traceback(self.code, "TypeError", f"Argument 1 of stdin has to be a variable pointer, not \"{arg}\"")
buf_size = 255
buffer = self.add_buffer(buf_size)
lines.append("mov rax, 0\n\t") # sys_read syscall
lines.append("mov rdi, 0\n\t") # a file descriptor of 0 is stdin
lines.append(f"mov rsi, {buffer}\n\t")
lines.append(f"mov rdx, {buf_size}")
lines.append("syscall")
if not self.current_var_scope.table.get(arg.var_name):
self.create_variable(lines, arg.var_name, buffer, StringNode)
else:
self.change_variable(lines, arg.var_name, buffer)
elif node.instruction == "jump":
self.clamp_instruction_args(node, 1, 1)
if not isinstance(node.arguments[0], LabelRefNode):
traceback(self.code, "TypeError", f"jump expects a label reference as the first argument, not \"{node.arguments[0]}\"")
self.lines.append(f"jmp .{node.arguments[0].name}\n\t")
lines.append(f"jmp .{node.arguments[0].name}\n\t")
elif node.instruction == "if":
self.clamp_instruction_args(node, 2, 2)
@@ -332,16 +465,17 @@ class X86_64Generator(Generator):
if isinstance(node.arguments[0], BoolNode):
if node.arguments[0].value:
self.lines.append(f"jmp .{node.arguments[1].name}\n\t")
#self.lines.append("mov eax, 1")
#self.lines.append(f"cmp eax, {1 if node.arguments[0].value else 0}")
lines.append(f"jmp .{node.arguments[1].name}\n\t")
#lines.append("mov eax, 1")
#lines.append(f"cmp eax, {1 if node.arguments[0].value else 0}")
elif type(node.arguments[0]) in [IntNode,FloatNode]:
if node.arguments[0].value != 0:
self.lines.append(f"jmp .{node.arguments[1].name}\n\t")
lines.append(f"jmp .{node.arguments[1].name}\n\t")
elif isinstance(node.arguments[0], VarRefNode):
self.get_variable(node.arguments[0].var_name, "eax")
self.lines.append(f"test eax, eax\n\t")
self.lines.append(f"jnz .{node.arguments[1].name}\n\t")
self.current_var_scope.table[node.arguments[0].var_name]["used"] = True # putting a variable in an if statement means we're accessing it
self.get_variable(lines, node.arguments[0].var_name, "eax")
lines.append(f"test eax, eax\n\t")
lines.append(f"jnz .{node.arguments[1].name}\n\t")
elif node.instruction in ["equal", "inequal", "greater", "lesser"]:
self.clamp_instruction_args(node, 3, 3)
@@ -356,34 +490,34 @@ class X86_64Generator(Generator):
arg2 = None
if isinstance(node.arguments[0], BoolNode):
self.lines.append(f"mov rax, {int(node.arguments[0].value)}\n\t")
lines.append(f"mov rax, {int(node.arguments[0].value)}\n\t")
arg1 = "rax"
elif isinstance(node.arguments[0], IntNode):
self.lines.append(f"mov rax, {node.arguments[0].value}\n\t")
lines.append(f"mov rax, {node.arguments[0].value}\n\t")
arg1 = "rax"
elif isinstance(node.arguments[0], FloatNode):
const_name = self.add_constant(node.arguments[0].value)
self.lines.append(f"movsd xmm0, {const_name}\n\t")
lines.append(f"movsd xmm0, {const_name}\n\t")
arg1 = "xmm0"
elif isinstance(node.arguments[0], VarRefNode):
self.get_variable(node.arguments[0].var_name, "rax")
self.get_variable(lines, node.arguments[0].var_name, "rax")
arg1 = "rax"
if isinstance(node.arguments[1], BoolNode):
self.lines.append(f"mov rbx, {int(node.arguments[1].value)}\n\t")
lines.append(f"mov rbx, {int(node.arguments[1].value)}\n\t")
arg2 = "rbx"
elif isinstance(node.arguments[1], IntNode):
self.lines.append(f"mov rbx, {node.arguments[1].value}\n\t")
lines.append(f"mov rbx, {node.arguments[1].value}\n\t")
arg2 = "rbx"
elif isinstance(node.arguments[1], FloatNode):
const_name = self.add_constant(node.arguments[1].value)
self.lines.append(f"movsd xmm1, {const_name}\n\t")
lines.append(f"movsd xmm1, {const_name}\n\t")
arg2 = "xmm1"
elif isinstance(node.arguments[1], VarRefNode):
self.get_variable(node.arguments[1].var_name, "rbx")
self.get_variable(lines, node.arguments[1].var_name, "rbx")
arg2 = "rbx"
self.lines.append(f"cmp {arg1}, {arg2}\n\t")
lines.append(f"cmp {arg1}, {arg2}\n\t")
instructions = {
"equal": "sete",
@@ -391,19 +525,170 @@ class X86_64Generator(Generator):
"greater": "setg",
"lesser": "setl"
}
self.lines.append(f"{instructions[node.instruction]} al\n\t")
self.lines.append("movzx rax, al\n\t")
lines.append(f"{instructions[node.instruction]} al\n\t")
lines.append("movzx eax, al\n\t")
var_name = node.arguments[2].var_name
if self.variables.get(var_name, None) == None:
self.create_variable(var_name, "rax", BoolNode)
if self.current_var_scope.lookup(var_name) == None:
self.create_variable(lines, var_name, "rax", BoolNode)
else:
self.change_variable(var_name, "rax")
self.change_variable(lines, var_name, "rax")
elif node.instruction == "not":
self.clamp_instruction_args(node, 2, 2)
if type(node.arguments[0]) not in [VarRefNode,BoolNode]:
traceback(self.code, "TypeError", f"Argument 1 of \"not\" must be a boolean or variable reference, not \"{node.arguments[0]}\"")
elif not isinstance(node.arguments[1], VarPointerNode):
traceback(self.code, "TypeError", f"Argument 2 of \"not\" must be a variable pointer, not \"{node.arguments[1]}\"")
result = ""
if isinstance(node.arguments[0], BoolNode):
result = BoolNode(not node.arguments[0].value)
elif isinstance(node.arguments[0], VarRefNode):
var_type = self.get_variable(lines, node.arguments[0].var_name, "rax")
if var_type != BoolNode:
traceback(self.code, "TypeError", f"Argument 1 of \"not\" must be a boolean or variable reference, not \"{var_type(0).__repr__()}\"")
result = "rax"
lines.append("xor rax, 1\n\t")
if self.current_var_scope.table.get(node.arguments[1].var_name):
self.change_variable(lines, node.arguments[1].var_name, result)
else:
self.lines.append("; FUCK\n\t")
#raise NotImplementedError(f"A generate method hasn't been made for the \"{node.instruction}\" instruction.")
self.create_variable(lines, node.arguments[1].var_name, result, BoolNode)
elif node.instruction == "endfun":
return
elif node.instruction == "return":
self.clamp_instruction_args(node, 0, 1)
func = self.functions.get(node.parent.name) # the return statement will be a parent of a function node, so we can get the current scope using that
func_scope: SymbolTable = func["scope"]
if len(node.arguments) == 1:
if isinstance(node.arguments[0], IntNode):
lines.append(f"mov rax, {node.arguments[0].value}")
elif isinstance(node.arguments[0], BoolNode):
lines.append(f"mov eax, {int(node.arguments[0].value)}")
elif isinstance(node.arguments[0], FloatNode):
lines.append(f"mov xmm0, {node.arguments[0].value}")
#self.get_variable(lines, node.arguments[0].var_name, "rax")
elif isinstance(node.arguments[0], StringNode):
string_pointer = self.add_constant(node.arguments[0].value)
length = self.add_constant(f"equ $ - {string_pointer[1:-1]}", no_string=True)
lines.append(f"lea rax, [rel {string_pointer[1:-1]}]\n\t")
lines.append(f"mov rdx, {length[1:-1]}\n\t")
#self.push("rax", lines)
elif isinstance(node.arguments[0], VarRefNode):
var = func_scope.lookup(node.arguments[0].var_name)
if var["type"] == StringNode:
self.get_variable(self.function_lines, node.arguments[0].var_name, "rax", offset=0, scope=func["scope"])
self.get_variable(self.function_lines, node.arguments[0].var_name, "rdx", offset=-8, scope=func["scope"])
elif var["type"] == BoolNode:
self.get_variable(self.function_lines, node.arguments[0].var_name, "eax", scope=func["scope"])
else:
self.get_variable(self.function_lines, node.arguments[0].var_name, "rax", scope=func["scope"])
else:
lines.append("mov rax, 0\n\t")
size = 0
for name, var in self.current_var_scope.table.items():
if type(var["stack_loc"]) == str:
continue # its in a register, we dont need to free up the stack
if var["type"] == StringNode:
size += 2
else:
size += 1
lines.append(f"add rsp, {8 * (size)}\n\t")
self.stack_size -= size - 1
lines.append("pop rbp")
lines.append("ret\n\t")
# warn about unused variables
for name, var in self.current_var_scope.table.items():
if not var["used"]:
warning(self.code, f"\"{name}\" was defined but never used.")
old_scope = self.current_var_scope
self.current_var_scope = self.current_var_scope.parent
del old_scope
elif node.instruction == "call":
self.clamp_instruction_args(node, 1, 2)
if not isinstance(node.arguments[0], FunctionCallNode):
traceback(self.code, "TypeError", "Argument 2 of call needs to be a function reference.")
func = self.functions.get(node.arguments[0].func_name, None)
if not func:
traceback(self.code, "TypeError", f"Function \"{node.arguments[0].func_name}\" is not defined.")
func_scope: SymbolTable = func["scope"]
func: FunctionNode = func["func"]
if len(self.arg_list) != len(func.args):
traceback(self.code, "TypeError", f"Function \"{node.arguments[0].func_name}\" takes {len(func.args)} arguments, but got {len(self.arg_list)}")
# stack alignment
if self.stack_size % 2 != 0:
lines.append("sub rsp, 8\n\t") # align the stack to 16 bytes
self.stack_size -= 1
for i, arg in enumerate(self.arg_list):
#self.create_variable(lines, func.args[i].name, arg, func.args[i].arg_type)
value = ""
var_type = ""
if isinstance(arg, IntNode):
value = arg.value
var_type = IntNode
elif isinstance(arg, StringNode):
value = self.add_constant(arg.value)
else:
traceback(self.code, "CallError", f"Can't pass {arg} to function.")
arg_name = func.args[i].name
#self.change_variable(lines, arg_name, value, func_scope)
if i == 0:
#func_scope.define(arg_name, {"stack_loc": "rdi", "type": type(arg)})
lines.append(f"mov rdi, {value}")
elif i == 1:
lines.append(f"mov rsi, {value}")
elif i == 2:
lines.append(f"mov rdx, {value}")
elif i == 3:
lines.append(f"mov rcx, {value}")
elif i == 4:
lines.append(f"mov r8, {value}")
elif i == 5:
lines.append(f"mov r9, {value}")
else:
traceback(self.code, "CallError", "Functions with more than 6 args aren't supported yet, sorry...")
lines.append(f"call {node.arguments[0].func_name}\n\t")
#if len(self.arg_list) > 0:
# lines.append(f"add rsp, {len(self.arg_list) * 8}")
self.stack_size += len(self.arg_list)
self.arg_list.clear()
if len(node.arguments) == 2:
if not isinstance(node.arguments[1], VarPointerNode):
traceback(self.code, "TypeError", "Argument 1 of call needs to be a variable pointer.")
if self.current_var_scope.lookup(node.arguments[1].var_name):
self.change_variable(lines, node.arguments[1].var_name, "rax")
else:
self.current_var_scope.define(node.arguments[1].var_name, {"stack_loc": "rax", "type": self.ground_type_to_node(func.return_type), "used": False})
#self.create_variable(lines, node.arguments[1].var_name, "rax", self.ground_type_to_node(self.functions.get(node.arguments[0].func_name)["func"].return_type))
elif node.instruction == "pusharg":
self.clamp_instruction_args(node, 1, 1)
#if type(node.arguments[0]) not in [IntNode]:
# traceback(self.code, "TypeError", f"A {node.arguments[0]} can't be passed as an argument.")
self.arg_list.append(node.arguments[0])
else:
raise NotImplementedError(f"A generate method hasn't been made for the \"{node.instruction}\" instruction.")
@@ -413,20 +698,34 @@ class X86_64Generator(Generator):
with open(self.output_path + ".asm", "w") as f:
f.write("; ~~~ Auto generated by the GroundPY compiler for Linux x86_64 targets. ~~~\n\n")
if len(self.constants) > 0:
f.write("section .data\n")
for name, const in self.constants.items():
value = const["value"]
f.write("." + name + ": ")
f.write(name + ": ")
value_type = type(value)
if value_type == str:
if not const["no_string"]:
f.write(f"db \"{value.replace("\\n","\", 10, \"")}\", 0".replace(", \"\", ", ", "))
value = value.replace("\"", "\", 34, \"")
value = value.replace("\r", "\", 13, \"")
value = value.replace("\n", "\", 10, \"")
value = value.replace("\a", "\", 7, \"")
final = f'db "' + value + "\", 0"
final = final.replace(", \"\", ", ", ")
f.write(final)
else:
f.write(value)
elif value_type == float or value_type == int:
f.write(f"dq {float(value)}")
f.write("\n")
if len(self.buffers) > 0:
f.write("section .bss\n")
for buf, size in self.buffers.items():
f.write(f"{buf} resb {size}\n")
f.write("section .text\n")
optimizer = X86_64Optimizer(self.lines)
optimizer = X86_64Optimizer(self.lines + self.function_lines)
f.writelines(optimizer.optimize_until_stable())

View File

@@ -119,7 +119,7 @@ def generate_ast(tokens: list[Token], code: str) -> RootNode:
scope = scope.parent # go up one scope
current_node.parent = scope
scope.statements.append(current_node)
current_node = scope
current_node = None
else:
current_node.parent = scope
else:
@@ -128,6 +128,9 @@ def generate_ast(tokens: list[Token], code: str) -> RootNode:
elif token.type == TokenType.LABEL_DECLERATION:
if current_node:
scope.statements.append(current_node)
if current_node_type == "func":
scope = current_node
current_node_type = None
current_node = None
scope.statements.append(LabelDecNode(token.value))

21
main.py
View File

@@ -5,15 +5,25 @@ from time import time
from generators import x86_64
from os import system, remove
from error import traceback
from argparse import ArgumentParser
def main():
in_path = "test2.grnd"
out_path = "out"
arch = "x86_64"
arg_parser = ArgumentParser(prog="GroundPY", description="A compiler for the Ground Programming Language.")
arg_parser.add_argument("input", help="ground source file path")
arg_parser.add_argument("--output", "-o", default="out", help="output file name")
arg_parser.add_argument("--arch", "-a", default="x86_64", choices=["x86_64"], help="the architecture to compile for")
arg_parser.add_argument("-S", "--save-asm", help="only generate an asm file, don't actually assemble it", action="store_true")
args = arg_parser.parse_args()
in_path = args.input
out_path = args.output
arch = args.arch
start = time()
try:
file = open(in_path, "r")
except FileNotFoundError:
traceback("", "fatal", f"no such file or directory \"{in_path}\"")
code = file.read()
file.close()
@@ -23,15 +33,14 @@ def main():
if arch == "x86_64":
generator = x86_64.X86_64Generator(ast, code, out_path)
else:
traceback(code, "fatal error", f"unkown architecture \"{arch}\"")
generator.init()
if not args.save_asm:
system(f"nasm -felf64 {out_path}.asm")
system(f"ld -m elf_{arch} -o {out_path} {out_path}.o")
remove(out_path + ".o")
#remove(out_path + ".asm")
remove(out_path + ".asm")
compile_time = time()-start
print(f"Compiled in {round(compile_time, 3)} seconds.")

View File

@@ -37,6 +37,7 @@ class Optimizer:
else:
if p_op != i_op:
return False
return True
def match_window(self, patterns: List[Instruction], window: List[Instruction]) -> Union[Dict[str, str], None]:
@@ -88,6 +89,7 @@ class Optimizer:
matched = True
break
if not matched:
#print('next')
i += 1
self.lines = [f"{line.opcode} {', '.join(line.operands)}\n" for line in self.lines]

View File

@@ -124,13 +124,42 @@ class X86_64Optimizer(Optimizer):
PeepholeRule(
match=[
Instruction("mov", ["a", "b"]),
Instruction("mov", ["c", "d"]),
Instruction("cmp", ["a", "d"])
Instruction("mov", ["rax", "y"]),
Instruction("mov", ["rbx", "x"]),
Instruction("cmp", ["rax", "rbx"])
],
replace=[
Instruction("mov", ["c", "d"]),
Instruction("cmp", ["b", "c"])
Instruction("mov", ["rax", "y"]),
Instruction("cmp", ["rax", "x"])
]
)
),
PeepholeRule(
match=[
Instruction("mov", ["x", "y"]),
Instruction("mov", ["y", "x"])
],
replace=[]
),
PeepholeRule(
match=[
Instruction("mov", ["x", "y"]),
Instruction("add", ["z", "x"])
],
replace=[
Instruction("add", ["z", "y"])
]
),
PeepholeRule(
match=[
Instruction("add", ["x", "0"])
],
replace=[]
),
PeepholeRule(
match=[Instruction("mov", ["x", "x"])],
replace=[]
),
]

BIN
out

Binary file not shown.

14
out.asm
View File

@@ -1,19 +1,5 @@
; ~~~ Auto generated by the GroundPY compiler for Linux x86_64 targets. ~~~
section .data
section .text
global _start
_start:
push 0
push 1
.loop:
mov rax, [rsp + 8]
add rax, 1
mov QWORD [rsp + 8], rax
mov rax, 100000000
mov rbx, [rsp + 8]
cmp rax, rbx
jg .loop
mov rax, 60
mov rdi, [rsp + 8]
syscall

7
test.c
View File

@@ -1,7 +0,0 @@
int main() {
int lol = 0;
for (int i = 0; i != 100000000; i++) {
lol++;
}
return lol;
}

View File

@@ -1,79 +0,0 @@
# checks if the current index in the determiner matches the current index in the target string
fun -bool !currentCharsMatch -string &str -string &determiner -int &counter -int &detIndex
getstrcharat $str $counter &currentChar
getstrcharat $determiner $detIndex &currentDetChar
equal $currentChar $currentDetChar &equals
return $equals
endfun
fun -list !split -string &str -string &determiner
# create an empty list
setlist *output ""
set &counter 0
set &detIndex 0
set &currentLoop "" # basically we build out the current token until we reach the determiner
getstrsize $str &length
getstrsize $determiner &determinerLength
# go through each char in the string, does it match the first char in our determiner?
#@loop
# we are technically getting the current char twice
# 1. inside the currentCharsMatch function
# 2. here
# but oh well, it wont be that bad (i hope)
getstrcharat $str $counter &currentChar
pusharg $str
pusharg $determiner
pusharg $counter
pusharg $detIndex
call !currentCharsMatch &equals
not $equals &doesntMatch
if $doesntMatch %next
stdlnout "WE HAVE A MATCH"
set &detIndex 0
# WE HAVE A MATCH BABY, we gotta make sure it matches the whole determiner tho
@innerLoop
pusharg $str
pusharg $determiner
pusharg $counter
pusharg $detIndex
call !currentCharsMatch &equals
add $detIndex 1 &detIndex
equal $detIndex $determinerLength &endOfDet
if $endOfDet %append
if $equals %innerLoop
@append
listappend *output $currentLoop
set &currentLoop ""
set &detIndex 0
@next
add $currentLoop $currentChar &currentLoop
# next character in the string
add $counter 1 &counter
inequal $length $counter &notAtEnd
stdlnout $notAtEnd
if $notAtEnd %loop
return *output
endfun
pusharg "split this string"
pusharg " "
call !split *listOut
#getlistsize *listOut &length
#stdlnout $length
set &counter 0
@loopOverList
getlistat *listOut $counter &currentItem
stdlnout $currentItem
add $counter 1 &counter
getlistsize *listOut &length
inequal $counter $length &notDone
if $notDone %loopOverList

47
test.s
View File

@@ -1,47 +0,0 @@
.file "test.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, -8(%rbp)
movl $0, -4(%rbp)
jmp .L2
.L3:
addl $1, -8(%rbp)
addl $1, -4(%rbp)
.L2:
cmpl $100000000, -4(%rbp)
jne .L3
movl -8(%rbp), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:

View File

@@ -1,7 +1,39 @@
set &var 0
set &cond true
set &number 4
set &stop 0
fun -double !mod -double &in -double &modulus
lesser $in $modulus &store
not $store &store
if $store %downloop
lesser $in 0 &store
if $store %uploop
jump %end
@downloop
subtract $in $modulus &in
lesser $in $modulus &store
not $store &store
if $store %downloop
jump %end
@uploop
add $in $modulus &in
lesser $in 0 &store
if $store %uploop
jump %end
@end
return $in
endfun
@loop
add $var 1 &var
greater 100000000 $var &cond
if $cond %loop
end $var
equal $number 1 &stop
if $stop %end
jump %loop
@end
end 0

View File

@@ -379,6 +379,7 @@ def tokenize(input_string: str):
current_char = input_string[pos]
while current_char != '"':
if current_char != "\\":
current_token += current_char
pos += 1
column += 1
@@ -387,6 +388,36 @@ def tokenize(input_string: str):
current_char = input_string[pos]
if current_char == "\\":
escape = ""
pos += 1
column += 1
while pos <= len(input_string)-1:
escape += input_string[pos]
valid_escapes = ['"', 'n', 't', 'a', 'r', '\\']
if escape == '"':
current_token += '"'
elif escape == "n":
current_token += '\n'
elif escape == "t":
current_token += '\t'
elif escape == "a":
current_token += "\a"
elif escape == "r":
current_token += "\r"
elif escape == "\\":
current_token += "\\"
if escape in valid_escapes:
break
pos += 1
column += 1
tokens.append(Token(
TokenType.STRING,
value=current_token