Skip to content

Commit

Permalink
Support global variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Veikko Soininen committed Oct 29, 2023
1 parent 1c02ced commit b95d356
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:

- name: Compile, Assemble, Link, and Run
run: |
INPUTS=("simple" "fn" "if" "if_2" "unary")
INPUTS=("simple" "fn" "if" "if_2" "unary" "global_int")
for FILE in "${INPUTS[@]}"; do
npm run start "tests/inputs/$FILE.c"
Expand Down
77 changes: 64 additions & 13 deletions src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,67 @@ import {
FunctionDeclarationNode,
ParameterNode,
IdentifierNode,
VariableDeclarationNode,
} from './ast';

interface CodeGenerationContext {
assemblyCode: string;
dataSegment: string;
bssSegment: string;
labelCounter: number;
variableMap: Map<string, number>;
globalVariables: Set<string>;
currentOffset: number;
}

function generateAssemblyCode(ast: ASTNode[]): string {
const context: CodeGenerationContext = {
assemblyCode: '',
dataSegment: 'section .data\n',
bssSegment: 'section .bss\n',
labelCounter: 0,
variableMap: new Map(),
globalVariables: new Set(),
currentOffset: 0,
};

ast.forEach((node) => {
generateProgram(context, node);
if (node.type === 'FunctionDeclaration') {
generateProgram(context, node);
} else if (node.type === 'VariableDeclaration') {
generateGlobalVariable(context, node);
} else {
throw new Error('Unsupported top-level node type');
}
});

return (
context.dataSegment +
context.bssSegment +
'section .text\nglobal main\n' +
context.assemblyCode
);
}

function generateGlobalVariable(
context: CodeGenerationContext,
node: VariableDeclarationNode,
): void {
if (node.value) {
if (node.value.type === 'Literal') {
context.dataSegment += `${node.identifier.value} dd ${node.value.value}\n`;
} else {
throw new Error(
`Unsupported global initializer of type: ${node.value.type}`,
);
}
} else {
context.bssSegment += `${node.identifier.value} resd 1\n`;
}

context.globalVariables.add(node.identifier.value);
}

function generateProgram(context: CodeGenerationContext, node: ASTNode): void {
if (node.type !== 'FunctionDeclaration') {
throw new Error('Program must contain a function declaration');
Expand Down Expand Up @@ -105,14 +136,18 @@ function generateStatement(
case 'Assignment':
{
generateExpression(context, node.value, parameters);
const offset = getOffsetFromEBP(
node.identifier,
parameters,
context,
);
context.assemblyCode += `mov [ebp ${
offset >= 0 ? '+' : ''
} ${offset}], eax\n`;
if (isGlobalVariable(context, node.identifier)) {
context.assemblyCode += `mov [${node.identifier.value}], eax\n`;
} else {
const offset = getOffsetFromEBP(
node.identifier,
parameters,
context,
);
context.assemblyCode += `mov [ebp ${
offset >= 0 ? '+' : ''
}${offset}], eax\n`;
}
}
break;

Expand Down Expand Up @@ -142,10 +177,14 @@ function generateExpression(
switch (node.type) {
case 'Identifier':
{
const offset = getOffsetFromEBP(node, parameters, context);
context.assemblyCode += `mov eax, [ebp ${
offset >= 0 ? '+' : ''
}${offset}]\n`;
if (isGlobalVariable(context, node)) {
context.assemblyCode += `mov eax, [${node.value}]\n`;
} else {
const offset = getOffsetFromEBP(node, parameters, context);
context.assemblyCode += `mov eax, [ebp ${
offset >= 0 ? '+' : ''
}${offset}]\n`;
}
}
break;

Expand Down Expand Up @@ -197,6 +236,13 @@ function isParameter(
);
}

function isGlobalVariable(
context: CodeGenerationContext,
identifier: IdentifierNode,
): boolean {
return context.globalVariables.has(identifier.value);
}

function generateCondition(
context: CodeGenerationContext,
node: ASTNode,
Expand All @@ -220,6 +266,11 @@ function getOffsetFromEBP(
parameters: ParameterNode[],
context: CodeGenerationContext,
): number {
if (isGlobalVariable(context, identifier)) {
throw new Error(
`Attempting to get EBP offset for global variable: ${identifier.value}`,
);
}
return isParameter(identifier, parameters)
? getParameterOffset(parameters, identifier)
: -getVariableOffsetFromEBP(identifier, context);
Expand Down
21 changes: 18 additions & 3 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,27 @@ class ParserContext {

export function parse(tokenList: Token[]): ASTNode[] {
const context = new ParserContext(tokenList);

const astNodes: ASTNode[] = [];

while (context.cursor < context.tokens.length) {
const functionNode = parseFunctionDeclaration(context);
astNodes.push(functionNode);
const currentToken = context.currentToken();
if (currentToken.type === TokenType.IntKeyword) {
const nextToken = context.tokens[context.cursor + 1];
if (nextToken.type === TokenType.Identifier) {
const afterNextToken = context.tokens[context.cursor + 2];
if (afterNextToken.type === TokenType.LeftParen) {
astNodes.push(parseFunctionDeclaration(context));
} else {
astNodes.push(parseVariableDeclaration(context));
}
} else {
throw new Error(
`Unexpected token after int keyword: ${nextToken.type}`,
);
}
} else {
throw new Error(`Unexpected token: ${currentToken.type}`);
}
}

return astNodes;
Expand Down
7 changes: 7 additions & 0 deletions tests/inputs/global_int.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
int y;
int x = 1;

int main() {
y = 4;
return y + x;
}

0 comments on commit b95d356

Please sign in to comment.