Skip to content

Commit

Permalink
Fix semantic and parser
Browse files Browse the repository at this point in the history
  • Loading branch information
danimal141 committed Dec 1, 2024
1 parent f0f888b commit f76f366
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 20 deletions.
6 changes: 4 additions & 2 deletions examples/hello-world.pgo
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package main {
print("hello world")
package main

func main(x int) {
print("hello")
}
14 changes: 11 additions & 3 deletions src/codegen/generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ describe("LLVMGenerator", () => {

describe("print statement", () => {
it("should generate correct IR for hello world", () => {
const input = `package main { print("hello") }`;
const input = `package main
func main() {
print("hello")
}`;
const ir = generateIR(input);

// Verify required components are present
const requiredParts = [
"declare i32 @printf",
"@.str.fmt = private unnamed_addr constant [3 x i8]", // Updated format string size
"@.str.fmt = private unnamed_addr constant [3 x i8]",
"define i32 @main()",
"call i32 (i8*, ...) @printf",
"ret i32 0",
Expand All @@ -37,7 +41,11 @@ describe("LLVMGenerator", () => {
});

it("should handle string with special characters", () => {
const input = `package main { print("hello\\nworld") }`;
const input = `package main
func main() {
print("hello\\nworld")
}`;
const ir = generateIR(input);

// Debug output
Expand Down
90 changes: 86 additions & 4 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {
FunctionDeclaration,
Identifier,
Location,
Parameter,
Program,
Statement,
StringLiteral,
TypeNode,
} from "./ast.ts";

export class Parser {
Expand Down Expand Up @@ -76,6 +78,79 @@ export class Parser {
}
}

/**
* Parse a type node
*/
private parseType(): TypeNode {
const location = this.currentLocation();
let typeType: "int" | "string" | "bool";

switch (this.currentToken.type) {
case TokenType.INT_TYPE:
typeType = "int";
break;
case TokenType.STRING_TYPE:
typeType = "string";
break;
case TokenType.BOOL_TYPE:
typeType = "bool";
break;
default:
this.throwError(`Expected type, got ${this.currentToken.type}`);
}

this.nextToken();
return {
type: "TypeNode",
typeType,
location,
};
}

/**
* Parse function parameters
*/
private parseFunctionParameters(): Parameter[] {
const parameters: Parameter[] = [];

// No parameters case: ()
if (this.currentToken.type === TokenType.RPAREN) {
return parameters;
}

// Parse first parameter
let param = this.parseParameter();
parameters.push(param);

// Parse additional parameters: ', parameter'*
while (this.currentToken.type === TokenType.COMMA) {
this.nextToken(); // consume comma
param = this.parseParameter();
parameters.push(param);
}

return parameters;
}

/**
* Parse a single parameter
*/
private parseParameter(): Parameter {
const location = this.currentLocation();

// Parse parameter name
const name = this.expectToken(TokenType.IDENT).literal;

// Parse parameter type
const paramType = this.parseType();

return {
name,
type: paramType,
location,
};
}

/**
* Main entry point for parsing a program
*/
Expand Down Expand Up @@ -114,19 +189,26 @@ export class Parser {
const name = this.expectToken(TokenType.IDENT).literal;

this.expectToken(TokenType.LPAREN);
// TODO: Parse parameters when we add support for them
const parameters = this.parseFunctionParameters();
this.expectToken(TokenType.RPAREN);

// TODO: Parse return type when we add support for it
let returnType: TypeNode | null = null;
if (
this.currentToken.type === TokenType.INT_TYPE ||
this.currentToken.type === TokenType.STRING_TYPE ||
this.currentToken.type === TokenType.BOOL_TYPE
) {
returnType = this.parseType();
}

this.expectToken(TokenType.LBRACE);
const body = this.parseBlockStatement();

return {
type: "FunctionDeclaration",
name,
parameters: [],
returnType: null,
parameters,
returnType,
body,
location,
};
Expand Down
36 changes: 25 additions & 11 deletions src/semantic/analyzer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { describe, it } from "std/testing/bdd.ts";
import { assertThrows } from "std/assert/mod.ts";
import { Parser } from "@/parser/parser.ts";
import { Lexer } from "@/lexer/lexer.ts";
import { SemanticAnalyzer, SemanticError } from "@/semantic/analyzer.ts";
import { SemanticError } from "@/semantic/analyzer.ts";
import { SemanticAnalyzer } from "@/semantic/analyzer.ts";

describe("SemanticAnalyzer", () => {
const parse = (input: string) => {
Expand All @@ -13,29 +14,37 @@ describe("SemanticAnalyzer", () => {

describe("print statement analysis", () => {
it("should accept valid string literal in print", () => {
const input = `package main { print("hello") }`;
const input = `package main
func main() {
print("hello")
}`;
const ast = parse(input);
const analyzer = new SemanticAnalyzer();

analyzer.analyze(ast);
analyzer.analyze(ast); // Should not throw
});

it("should reject print without arguments", () => {
const input = `package main { print() }`;
const input = `package main
func main() {
print()
}`;
const ast = parse(input);
const analyzer = new SemanticAnalyzer();

assertThrows(
() => analyzer.analyze(ast),
SemanticError,
"Function print expects 1 arguments",
"Function print expects 1 argument",
);
});
});

describe("package validation", () => {
it("should reject non-main package", () => {
const input = `package other { print("hello") }`;
const input = `package other
func main() {
print("hello")
}`;
const ast = parse(input);
const analyzer = new SemanticAnalyzer();

Expand All @@ -49,15 +58,20 @@ describe("SemanticAnalyzer", () => {

describe("main function validation", () => {
it("should accept valid main function", () => {
const input = `package main { print("hello") }`;
const input = `package main
func main() {
print("hello")
}`;
const ast = parse(input);
const analyzer = new SemanticAnalyzer();

analyzer.analyze(ast);
analyzer.analyze(ast); // Should not throw
});

it("should reject main function with parameters", () => {
const input = `package main func main(x int) { print("hello") }`;
const input = `package main
func main(x int) {
print("hello")
}`;
const ast = parse(input);
const analyzer = new SemanticAnalyzer();

Expand Down

0 comments on commit f76f366

Please sign in to comment.