Skip to content

Commit

Permalink
Merge pull request #26 from kntkymt/feature/globalVariable
Browse files Browse the repository at this point in the history
ステップ23: グローバル変数を実装する
  • Loading branch information
kntkymt authored Dec 16, 2023
2 parents 6f36b33 + 40f8f81 commit 28f1bfe
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 110 deletions.
51 changes: 37 additions & 14 deletions Sources/Generator/Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public final class Generator {
var addressOffset: Int
}

private var globalVariables: [String: any TypeNodeProtocol] = [:]
private var variables: [String: VariableInfo] = [:]
private var functionLabels: Set<String> = Set()

Expand All @@ -39,14 +40,31 @@ public final class Generator {
// MARK: - Public

public func generate(sourceFileNode: SourceFileNode) throws -> String {
var result = ""

var variableDeclResult = ""
for variableDecl in sourceFileNode.globalVariables {
variableDeclResult += try generateGlobalVariableDecl(node: variableDecl)
}

var functionDeclResult = ""
for functionDecl in sourceFileNode.functions {
result += try generate(node: functionDecl)
functionDeclResult += try generate(node: functionDecl)
}

let functionMeta = ".globl \(functionLabels.joined(separator: ", "))\n"

return functionMeta + result
return variableDeclResult + functionMeta + functionDeclResult
}

func generateGlobalVariableDecl(node: VariableDeclNode) throws -> String {
var result = ""

// g: .zeroじゃApple Clangは動かない?
result += ".comm g,\(node.type.memorySize),8\n"

globalVariables[node.identifierName] = node.type

return result
}

func generate(node: any NodeProtocol) throws -> String {
Expand All @@ -64,7 +82,7 @@ public final class Generator {
let casted = try node.casted(IdentifierNode.self)

// アドレスをpush
result += try generatePushLocalVariableAddress(node: casted)
result += try generatePushVariableAddress(node: casted)

// 配列の場合は先頭アドレスのまま返す
// 配列以外の場合はアドレスの中身を返す
Expand Down Expand Up @@ -123,7 +141,7 @@ public final class Generator {

var parameterDecl = ""
// 引数をローカル変数として保存し直す
for (index, parameter) in casted.parameters.enumerated() {
for (index, parameter) in casted.parameterNodes.enumerated() {
let offset = (variables.count + 1) * 8
variables[parameter.identifierName] = VariableInfo(type: parameter.type, addressOffset: offset)

Expand Down Expand Up @@ -157,7 +175,7 @@ public final class Generator {
case .subscriptCallExpr:
let casted = try node.casted(SubscriptCallExpressionNode.self)

result += try generatePushLocalArrayElementAddress(node: casted)
result += try generatePushArrayElementAddress(node: casted)

// 結果のアドレスの値をロードしてスタックに積む
result += " ldr x0, [sp]\n"
Expand Down Expand Up @@ -323,7 +341,7 @@ public final class Generator {
case .address:
// &のあとは変数しか入らない(はず?)
if let right = casted.right as? IdentifierNode {
result += try generatePushLocalVariableAddress(node: right)
result += try generatePushVariableAddress(node: right)
} else {
throw GenerateError.invalidSyntax(index: node.sourceTokens[0].sourceIndex)
}
Expand All @@ -337,11 +355,11 @@ public final class Generator {
if casted.operator is AssignNode {
// 左辺は変数, `*値`, subscriptCall
if casted.left is IdentifierNode {
result += try generatePushLocalVariableAddress(node: casted.left.casted(IdentifierNode.self))
result += try generatePushVariableAddress(node: casted.left.casted(IdentifierNode.self))
} else if let pointer = casted.left as? PrefixOperatorExpressionNode, pointer.operatorKind == .reference {
result += try generate(node: pointer.right)
} else if let subscriptCall = casted.left as? SubscriptCallExpressionNode {
result += try generatePushLocalArrayElementAddress(node: subscriptCall)
result += try generatePushArrayElementAddress(node: subscriptCall)
} else {
throw GenerateError.invalidSyntax(index: casted.left.sourceTokens[0].sourceIndex)
}
Expand Down Expand Up @@ -442,24 +460,29 @@ public final class Generator {
// MARK: - Private

/// nameの変数のアドレスをスタックにpushするコードを生成する
private func generatePushLocalVariableAddress(node: IdentifierNode) throws -> String {
private func generatePushVariableAddress(node: IdentifierNode) throws -> String {
var result = ""

guard let offset = variables[node.identifierName]?.addressOffset else {
if let localVariableInfo = variables[node.identifierName] {
result += " sub x0, x29, #\(localVariableInfo.addressOffset)\n"
} else if globalVariables[node.identifierName] != nil {
// addじゃなくてldrであってる?
result += " adrp x0, \(node.identifierName)@GOTPAGE\n"
result += " ldr x0, [x0, \(node.identifierName)@GOTPAGEOFF]\n"
} else {
throw GenerateError.noSuchVariable(varibaleName: node.identifierName, index: node.token.sourceIndex)
}

result += " sub x0, x29, #\(offset)\n"
result += " str x0, [sp, #-16]!\n"

return result
}

private func generatePushLocalArrayElementAddress(node: SubscriptCallExpressionNode) throws -> String {
private func generatePushArrayElementAddress(node: SubscriptCallExpressionNode) throws -> String {
var result = ""

// 配列の先頭アドレス, subscriptの値をpush
result += try generatePushLocalVariableAddress(node: node.identifierNode)
result += try generatePushVariableAddress(node: node.identifierNode)
result += try generate(node: node.argument)

result += " ldr x0, [sp]\n"
Expand Down
54 changes: 38 additions & 16 deletions Sources/Parser/Node/StatementNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,26 +131,42 @@ public class FunctionDeclNode: NodeProtocol {
// MARK: - Property

public var kind: NodeKind = .functionDecl
public let sourceTokens: [Token]
public var children: [any NodeProtocol] { [block] + parameters }
public var sourceTokens: [Token] {
returnTypeNode.sourceTokens +
[functionNameToken, parenthesisLeftToken] +
parameterNodes.flatMap { $0.sourceTokens } +
[parenthesisRightToken] +
block.sourceTokens
}
public var children: [any NodeProtocol] { [returnTypeNode] + parameterNodes + [block] }

public let returnType: any NodeProtocol
public let token: Token
public let returnTypeNode: any NodeProtocol
public let functionNameToken: Token
public let parenthesisLeftToken: Token
public let parameterNodes: [VariableDeclNode]
public let parenthesisRightToken: Token
public let block: BlockStatementNode
public let parameters: [VariableDeclNode]

public var functionName: String {
token.value
functionNameToken.value
}

// MARK: - Initializer

init(returnType: any NodeProtocol, token: Token, block: BlockStatementNode, parameters: [VariableDeclNode], sourceTokens: [Token]) {
self.returnType = returnType
self.token = token
init(
returnTypeNode: any NodeProtocol,
functionNameToken: Token,
parenthesisLeftToken: Token,
parameterNodes: [VariableDeclNode],
parenthesisRightToken: Token,
block: BlockStatementNode
) {
self.returnTypeNode = returnTypeNode
self.functionNameToken = functionNameToken
self.parenthesisLeftToken = parenthesisLeftToken
self.parameterNodes = parameterNodes
self.parenthesisRightToken = parenthesisRightToken
self.block = block
self.parameters = parameters
self.sourceTokens = sourceTokens
}
}

Expand All @@ -162,7 +178,7 @@ public class VariableDeclNode: NodeProtocol {
public var sourceTokens: [Token] {
type.sourceTokens + [identifierToken]
}
public let children: [any NodeProtocol] = []
public var children: [any NodeProtocol] { [type] }

public let type: any TypeNodeProtocol
public let identifierToken: Token
Expand All @@ -184,15 +200,21 @@ public class SourceFileNode: NodeProtocol {
// MARK: - Property

public var kind: NodeKind = .sourceFile
public let sourceTokens: [Token]
public var children: [any NodeProtocol] { functions }
public var sourceTokens: [Token] {
functions.flatMap { $0.sourceTokens } + globalVariables.flatMap { $0.sourceTokens }
}

public var children: [any NodeProtocol] {
functions + globalVariables
}

public let functions: [FunctionDeclNode]
public let globalVariables: [VariableDeclNode]

// MARK: - Initializer

init(functions: [FunctionDeclNode], sourceTokens: [Token]) {
init(functions: [FunctionDeclNode], globalVariables: [VariableDeclNode]) {
self.functions = functions
self.sourceTokens = sourceTokens
self.globalVariables = globalVariables
}
}
59 changes: 30 additions & 29 deletions Sources/Parser/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,43 +105,44 @@ public final class Parser {

// MARK: - Syntax

// program = functionDecl*
// program = (functionDecl | variableDecl ";")*
func program() throws -> SourceFileNode {
var functionDecls: [FunctionDeclNode] = []
var globalVariableDecls: [VariableDeclNode] = []

while index < tokens.count {
functionDecls.append(try functionDecl())
}

return SourceFileNode(functions: functionDecls, sourceTokens: tokens)
}
let type = try type()
let identifier = try consumeIdentifierToken()

// functionDecl = type ident "(" functionParameters? ")" block
func functionDecl() throws -> FunctionDeclNode {
if index >= tokens.count {
throw ParseError.invalidSyntax(index: tokens.last.map { $0.sourceIndex + 1 } ?? 0)
}
let startIndex = index
// functionDecl = type ident "(" functionParameters? ")" block
if index < tokens.count, case .reserved(.parenthesisLeft, _) = tokens[index] {
let parenthesisLeft = try consumeReservedToken(.parenthesisLeft)

let returnType = try type()
var parameters: [VariableDeclNode] = []
if index < tokens.count, case .type = tokens[index] {
parameters = try functionParameters()
}

let functionName = try consumeIdentifierToken()
try consumeReservedToken(.parenthesisLeft)
let parenthesisRight = try consumeReservedToken(.parenthesisRight)

var parameters: [VariableDeclNode] = []
if index < tokens.count, case .type = tokens[index] {
parameters = try functionParameters()
let function = FunctionDeclNode(
returnTypeNode: type,
functionNameToken: identifier,
parenthesisLeftToken: parenthesisLeft,
parameterNodes: parameters,
parenthesisRightToken: parenthesisRight,
block: try block()
)
functionDecls.append(function)
} else {
let variable = try variableDecl(variableType: type, identifier: identifier)
try consumeReservedToken(.semicolon)
globalVariableDecls.append(variable)
}
}

try consumeReservedToken(.parenthesisRight)

return FunctionDeclNode(
returnType: returnType,
token: functionName,
block: try block(),
parameters: parameters,
sourceTokens: Array(tokens[startIndex..<index])
)
return SourceFileNode(functions: functionDecls, globalVariables: globalVariableDecls)
}

// functionParameters = variableDecl ("," variableDecl)*
Expand Down Expand Up @@ -282,9 +283,9 @@ public final class Parser {
}

// variableDecl = type identifier ("[" num "]")?
func variableDecl() throws -> VariableDeclNode {
var type = try type()
let identifier = try consumeIdentifierToken()
func variableDecl(variableType: (any TypeNodeProtocol)? = nil, identifier: Token? = nil) throws -> VariableDeclNode {
var type = if let variableType { variableType } else { try type() }
let identifier = if let identifier { identifier } else { try consumeIdentifierToken() }

if index < tokens.count, case .reserved(.squareLeft, _) = tokens[index] {
type = ArrayTypeNode(
Expand Down
Loading

0 comments on commit 28f1bfe

Please sign in to comment.