Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup #74

Merged
merged 25 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e2a44fe
Refactor lexer with Black
thedavidchu Jul 12, 2024
21fa91a
Refactor parser with Black
thedavidchu Jul 12, 2024
d28c3f0
Refactor analyzer with Black
thedavidchu Jul 12, 2024
592c7c7
Refactor emitter with Black
thedavidchu Jul 12, 2024
e276545
Refactor test with Black
thedavidchu Jul 12, 2024
90fe871
Add error printing function
thedavidchu Jul 12, 2024
ef1149c
Change up stuff
thedavidchu Jul 13, 2024
8e5304f
Add automatic check for error printing
thedavidchu Jul 13, 2024
c8f34ee
Simplify adding compiler to sys path
thedavidchu Jul 13, 2024
b67299e
Rename error.py to lol_error.py
thedavidchu Jul 13, 2024
92e7902
Rename TokenType -> LolTokenType and Token -> LolToken
thedavidchu Jul 13, 2024
ae8921a
Rename lol_error back to error
thedavidchu Jul 13, 2024
c19e293
Remove line and col from char stream
thedavidchu Jul 13, 2024
f966ab7
Add support for str err
thedavidchu Jul 13, 2024
e192917
Change main file location
thedavidchu Jul 13, 2024
01711c0
Comment bad token
thedavidchu Jul 13, 2024
aa556a3
Enable ==
thedavidchu Jul 13, 2024
b28b634
Delete dead code
thedavidchu Jul 13, 2024
0ee3dca
Add error messaging to lexer
thedavidchu Jul 14, 2024
bfbafff
Remove get_text func
thedavidchu Jul 14, 2024
92b5e1d
Parse with path
thedavidchu Jul 14, 2024
58d10ad
Add return hint
thedavidchu Jul 14, 2024
2dd8633
Simplify position and text
thedavidchu Jul 14, 2024
9ffeb88
Generalize to_dict function for parser
thedavidchu Jul 14, 2024
906a2e9
Add start and end positions to parser types
thedavidchu Jul 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ jobs:
- name: Compile
run: |
# For some reason, if we change to the compiler directory, Python complains.
export PYTHONPATH="${PYTHONPATH}:/home/runner/work/dolang/dolang/src/"
for x in fibonacci helloworld math_ops nested_if sum_three
do
python src/compiler/lol.py -i examples/$x.lol -o results
python src/main.py -i examples/$x.lol -o results
gcc results/$x-*.c
./a.out
done
5 changes: 5 additions & 0 deletions examples/invalid/bad_token.lol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* Bad token */
function main() -> i32 {
let a: i32 = 1 ++ 2; /* Bad token here */
return 0;
}
204 changes: 147 additions & 57 deletions src/compiler/analyzer/lol_analyzer.py

Large diffs are not rendered by default.

50 changes: 35 additions & 15 deletions src/compiler/emitter/lol_emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,24 @@
1. Minimal Viable Product
2. Correct indentation
"""

from typing import List

from compiler.analyzer.lol_analyzer import (
LolAnalysisModule, LolAnalysisFunction, LolAnalysisBuiltinType,
LolIRReturnStatement, LolIRFunctionCallStatement, LolIRDefinitionStatement,
LolIRSetStatement, LolIRIfStatement,
LolIRExpression, LolIRStatement,
LolIRFunctionCallExpression, LolIROperatorExpression,
LolIRLiteralExpression, LolAnalysisVariable
LolAnalysisModule,
LolAnalysisFunction,
LolAnalysisBuiltinType,
LolIRReturnStatement,
LolIRFunctionCallStatement,
LolIRDefinitionStatement,
LolIRSetStatement,
LolIRIfStatement,
LolIRExpression,
LolIRStatement,
LolIRFunctionCallExpression,
LolIROperatorExpression,
LolIRLiteralExpression,
LolAnalysisVariable,
)


Expand Down Expand Up @@ -45,25 +54,25 @@ def emit_expr(expr: LolIRExpression) -> str:
elif isinstance(expr, LolIRLiteralExpression):
literal = expr.literal
if isinstance(literal, str):
return f"\"{literal}\""
return f'"{literal}"'
elif isinstance(literal, int):
return f"{expr.literal}"
elif isinstance(expr, LolAnalysisVariable):
return f"{mangle_var_name(expr.name)}"


def emit_statements(
ir_statements: List[LolIRStatement],
*,
indentation: str = " "
ir_statements: List[LolIRStatement], *, indentation: str = " "
) -> List[str]:
statements: List[str] = []
for stmt in ir_statements:
if isinstance(stmt, LolIRDefinitionStatement):
var_name = mangle_var_name(stmt.name)
var_type = lol_to_c_types[stmt.type.name]
var_value = emit_expr(stmt.value)
statements.append(indentation + f"{var_type} {var_name} = {var_value};")
statements.append(
indentation + f"{var_type} {var_name} = {var_value};"
)
elif isinstance(stmt, LolIRSetStatement):
var_name = mangle_var_name(stmt.name)
var_value = emit_expr(stmt.value)
Expand All @@ -75,15 +84,26 @@ def emit_statements(
name = mangle_var_name(stmt.ret_var.name)
statements.append(indentation + f"return {name};")
elif isinstance(stmt, LolIRIfStatement):
statements.append(indentation + f"if ({mangle_var_name(stmt.if_cond.name)}) {{")
statements.extend(emit_statements(stmt.if_body, indentation=indentation + " "))
statements.append(
indentation + f"if ({mangle_var_name(stmt.if_cond.name)}) {{"
)
statements.extend(
emit_statements(stmt.if_body, indentation=indentation + " ")
)
statements.append(indentation + "} else {")
statements.extend(emit_statements(stmt.else_body, indentation=indentation + " "))
statements.extend(
emit_statements(
stmt.else_body, indentation=indentation + " "
)
)
statements.append(indentation + "}")
else:
raise ValueError("unrecognized statement type (maybe if statement?)")
raise ValueError(
"unrecognized statement type (maybe if statement?)"
)
return statements


def emit_function(func: LolAnalysisFunction):
prototype = (
f"{lol_to_c_types[func.return_types.name]}\n"
Expand Down
136 changes: 136 additions & 0 deletions src/compiler/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from pathlib import Path


class LolError:
def __init__(
self,
input_: Path | str,
start_position: int,
end_position: int,
message: str,
):
self.error_string = LolError.create_error_string(
input_, start_position, end_position, message
)

self.input_ = input_
self.start_position = start_position
self.end_position = end_position
self.message = message

def __str__(self) -> str:
return self.error_string

def __repr__(self) -> str:
return self.error_string

@staticmethod
def print_error(
input_: Path | str,
start_position: int,
end_position: int,
message: str,
):
error_string = LolError.create_error_string(
input_, start_position, end_position, message
)
print(error_string)

def get_text_of_interest(self) -> str:
"""
@brief This function returns the text that we intended to highlight.

@note This intention of this function is for debugging.
"""
with self.input_.open() as f:
text = f.read()

return text[self.start_position : self.end_position]

@staticmethod
def get_position(text: str, lineno: int, colno: int) -> tuple[int, int]:
# We want to take the first 'lineno' lines (i.e. we want a list
# of length 'lineno' lines).
lines = text.splitlines()[: lineno + 1]
assert len(lines) == lineno
# Similarly, we want the line of interest (i.e. the last line)
# to be a string of 'colno' characters.
lines[-1] = lines[-1][: colno + 1]
assert len(lines[-1]) == colno
return len("\n".join(lines))

@staticmethod
def get_line_and_column(text: str, position: int) -> tuple[int, int]:
"""
@note I index from zero.
Source: https://stackoverflow.com/questions/24495713/python-get-the-line-and-column-number-of-string-index
"""
# NOTE A new line character ends the line, so we do not count the
# character itself in the count. I do not believe this
# implements this property.
# i.e. "Hello, World!\n"
# "Good-bye, World!\n"
if not len(text):
return 0, 0
sp = text[:position].splitlines()
lineno, colno = len(sp) - 1, len(sp[-1])
return lineno, colno

@staticmethod
def create_single_line_error_string(
input_text: str, lineno: int, start_col: int, end_col: int, message: str
) -> str:
"""Create an error string for an error that spans a single line."""
text_lines: list[str] = input_text.splitlines()
user_line = lineno + 1
return (
f"> Error: {message}\n"
f"> {user_line} | {text_lines[lineno]}\n"
# NOTE I just guessed for the number of spaces and '^'
f"> {' ' * len(str(user_line))} | {' ' * (start_col)}{'^' * (end_col-start_col)}\n"
)

@staticmethod
def create_error_string(
input_: Path | str,
start_position: int,
end_position: int,
message: str,
) -> str:
"""
@brief Create a string with the error underlined and the error message displayed.

@param start_position: int
The very first character that should be highlighted.
@param end_position: int
One past the last character that should be highlighted.

@example
> Error: Expecting expression, got '{'
> 10 |
> 11 | if {
> ^
> 12 | io::printf("Hello, World!\n");
> 13 | }
>
"""
assert start_position < end_position

if isinstance(input_, Path):
with input_.open() as f:
input_text: str = f.read()
elif isinstance(input_, str):
input_text = input_
start_line, start_col = LolError.get_line_and_column(
input_text, start_position
)
end_line, end_col = LolError.get_line_and_column(
input_text, end_position
)

if start_line == end_line:
return LolError.create_single_line_error_string(
input_text, start_line, start_col, end_col, message
)
else:
raise NotImplementedError
Loading