Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
109 changes: 57 additions & 52 deletions src/parser/DeclParser.cj
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ internal open class DeclParser {
if (let Some(funcDef) <- functionDefinition()) {
return funcDef
}
} catch (error: ParseError) {
} catch (error: ParseError | ConsumeError) {
helper.synchronize()
}
None
}

private func unnamedParameterList(): ArrayList<FuncParam> {
let parameters: ArrayList<FuncParam> = ArrayList()
let start: (Token, Int64) = (helper.previous(), helper.current - 1)
let start: (Token, Int64) = helper.previousNonNLTokenWithIndex()
if (helper.check(TK.IDENTIFIER)) {
do {
if (helper.check(TK.IDENTIFIER)) {
Expand Down Expand Up @@ -123,20 +123,21 @@ internal open class DeclParser {
return ArrayList()
}
helper.consume(TK.LPAREN, "Expect '(' before function parameters", helper.current)
let start: (Token, Int64) = (helper.previous(), helper.current - 1)
let start: (Token, Int64) = helper.previousNonNLTokenWithIndex()
helper.skipNL()
let parameters: ArrayList<FuncParam> = ArrayList()
try {
parameters.add(all: unnamedParameterList())
helper.skipNL()
helper.consume(TK.RPAREN, "Expect ')' after function parameters", helper.current)
helper.skipNL()
} catch (e: ConsumeError) {
if (helper.check(TK.LCURL)) {
reporter.pop()
reporter.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
} else {
helper.synchronizeBlockParenthesis(start[0], start[1])
try {
helper.consume(TK.RPAREN, "Expect ')' after function parameters", helper.current)
helper.skipNL()
} catch (e: ConsumeError) {
if (helper.check(TK.LCURL)) {
helper.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
} else {
helper.synchronizeBlockParenthesis(start[0], start[1])
}
}
} catch (e: ParseError) {
helper.synchronizeBlockParenthesis(start[0], start[1])
Expand Down Expand Up @@ -196,22 +197,25 @@ internal open class DeclParser {
try {
helper.consume(TK.RCURL, "Expect '}' after block.", helper.current)
} catch (e: ConsumeError) {
helper.synchronizeBlockRightCurl(start[0], start[1])
reporter.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
helper.trySynchronizeBlockRightCurlOrError(helper.peek(), start[0], start[1], helper.current)
}
Body(NodeIdManager.nextId(), decls)
}

public func classMemberDeclaration(): ?Decl {
var decl: ?Decl = None
if (let Some(classInit) <- classInit()) {
decl = classInit
}
if (let Some(variableDeclaration) <- variableDeclaration()) {
decl = variableDeclaration
}
if (let Some(functionDefinition) <- functionDefinition()) {
decl = functionDefinition
try {
if (let Some(classInit) <- classInit()) {
decl = classInit
}
if (let Some(variableDeclaration) <- variableDeclaration()) {
decl = variableDeclaration
}
if (let Some(functionDefinition) <- functionDefinition()) {
decl = functionDefinition
}
} catch (e: ParseError | ConsumeError) {
helper.synchronize()
}
decl
}
Expand Down Expand Up @@ -303,19 +307,21 @@ internal open class DeclParser {
try {
helper.consume(TK.RCURL, "Expect '}' after block.", helper.current)
} catch (e: ConsumeError) {
helper.synchronizeBlockRightCurl(start[0], start[1])
reporter.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
helper.trySynchronizeBlockRightCurlOrError(helper.peek(), start[0], start[1], helper.current)
}

Body(NodeIdManager.nextId(), decls)
}

private func interfaceMemberDeclaration(): ?Decl {
if (let Some(functionDefinition) <- functionDefinition()) {
functionDefinition
} else {
None
try {
if (let Some(functionDefinition) <- functionDefinition()) {
functionDefinition
}
} catch (e: ParseError | ConsumeError) {
helper.synchronize()
}
None
}

private func superClass(): TypeNode {
Expand Down Expand Up @@ -423,16 +429,18 @@ internal open class DeclParser {

var nodes: ArrayList<Node> = ArrayList()
helper.consume(TK.LCURL, "Expect '{' before block.", helper.current)
let start: (Token, Int64) = (helper.peek(), helper.current)
let start: (Token, Int64) = (helper.previous(), helper.current - 1)
try {
nodes = expressionOrDeclarations()
helper.endStar()
helper.consume(TK.RCURL, "Expect '}' after block.", helper.current)
} catch (e: ParseError) {
helper.synchronizeBlockRightCurl(start[0], start[1])
reporter.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
} catch (e: ConsumeError) {
reporter.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
} catch (e: ParseError | ConsumeError) {
// ParserError comes from expressionOrDeclaration(),
// and ConsumeError comes from helper.consume(...),
// both indicating something invalid appears in the block,
// which means the block is either missing a '}',
// or containing TLDs.
helper.trySynchronizeBlockRightCurlOrError(helper.peek(), start[0], start[1], helper.current)
}
Block(NodeIdManager.nextId(), nodes)
}
Expand Down Expand Up @@ -461,23 +469,18 @@ internal open class DeclParser {
}

private func expressionOrDeclaration(): ?Node {
try {
if (let Some(decl) <- declaration()) {
return decl
}
if (let Some(expr) <- expression()) {
return expr
}
if (helper.matches(TKH.END) || helper.check(TK.RCURL)) {
return None
}
throw helper.error(helper.peek(),
"Expected expression or declaration inside block, found '\u{001b}[31m${helper.peek().value}\u{001b}[0m'",
helper.current)
} catch (e: ParseError) {
helper.synchronize()
if (let Some(decl) <- declaration()) {
return decl
}
None
if (let Some(expr) <- expression()) {
return expr
}
if (helper.matches(TKH.END) || helper.check(TK.RCURL)) {
return None
}
throw helper.error(helper.peek(),
"Expected expression or declaration inside block, found '\u{001b}[31m${helper.peek().value}\u{001b}[0m'",
helper.current)
}

private func whileExpr(): ?Expr {
Expand Down Expand Up @@ -658,11 +661,13 @@ internal open class DeclParser {
helper.skipNL()
} while (helper.matches(TK.COMMA))
}
helper.consume(TK.RPAREN, "Expect ')' after arguments.", helper.current)
try {
helper.consume(TK.RPAREN, "Expect ')' after arguments.", helper.current)
} catch (e: ConsumeError) {
helper.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
}
} catch (e: ParseError) {
helper.synchronizeCallParenthesis(start[0], start[1])
} catch (e: ConsumeError) {
reporter.unclosedDelimiterError(helper.peek(), start[0], start[1], helper.current)
}
} else if (!helper.matches(TK.UNIT_LITERAL)) {
return None
Expand Down
43 changes: 20 additions & 23 deletions src/parser/ErrorMessage.cj
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,28 @@ ${spaces} | ${" " * suffixPrefix[0].size}^
}

public class ErrorUnclosedDelimiter <: BaseErrorMessage {
ErrorUnclosedDelimiter(public let line: Int32, public let startIndex: Int64, public let start: String,
public let suffixPrefixStart: (String, String), public let currentIndex: Int64, public let current: String,
public let suffixPrefixCurrent: (String, String)) {}
ErrorUnclosedDelimiter(public let startLine: Int32, public let startIndex: Int64, public let start: String,
public let suffixPrefixStart: (String, String), public let currentLine: Int32, public let currentIndex: Int64,
public let current: String, public let suffixPrefixCurrent: (String, String), public let end: String) {}

public func toString(): String {
let lineStr: String = line.toString()
let spaces: String = " " * lineStr.size
if (currentIndex - startIndex < 20) {
return """
\u{001b}[31m
error\u{001b}[0m: unclosed delimiter '${Reporter.redify(start)}'
${spaces} |
${lineStr} | ${suffixPrefixStart[0]}${Reporter
.redify(start)} ${suffixPrefixStart[1]}
${spaces} | ${" " * suffixPrefixStart[0].size}^
"""
} else {
return """
let startLineStr: String = startLine.toString()
let startSpaces: String = " " * startLineStr.size
let currentLineStr: String = currentLine.toString()
let currentSpaces: String = " " * currentLineStr.size
return """
\u{001b}[31m
error\u{001b}[0m: unclosed delimiter '${Reporter.redify(start)}'
${spaces} |
${lineStr} | ${suffixPrefixStart[0]}${Reporter
.redify(start)} ${suffixPrefixStart[1]}
${spaces} | ${" " * suffixPrefixStart[0].size}^
"""
}
error\u{001b}[0m: unclosed delimiter: '${Reporter.redify(start)}'
${startSpaces} |
${startLineStr} | ${suffixPrefixStart[0]}${Reporter
.redify(start)} ${suffixPrefixStart[1]}
${startSpaces} | ${" " * suffixPrefixStart[0].size}^
\u{001b}[36minfo\u{001b}[0m: reached '${Reporter
.redify(current)}' without closing '${Reporter.redify(end)}'
${currentSpaces} |
${currentLineStr} | ${suffixPrefixCurrent[0]}${Reporter
.redify(current)} ${suffixPrefixCurrent[1]}
${currentSpaces} | ${" " * suffixPrefixCurrent[0].size}^
"""
}
}
10 changes: 9 additions & 1 deletion src/parser/Parser.cj
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ public class Parser {
} else {
return None
}
} catch (_) {
} catch (_: FinalError) {
return None
} catch (e: Exception) {
let builder = StringBuilder()
for (elem in e.getStackTrace()) {
builder.append("\n\tat ${elem.declaringClass}::${elem.methodName}(${elem.fileName}:${elem.lineNumber})")
}
parserHelper.error(parserHelper.peek(),
"Internal error during parsing:\n${e.toString()}${builder.toString()}", parserHelper.current)
return None
}
}
Expand Down
63 changes: 39 additions & 24 deletions src/parser/ParserHelper.cj
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ class ParserHelper {
tokens[current_ - 1]
}

func previousNonNLTokenWithIndex(): (Token, Int64) {
var idx: Int64 = current_ - 1
while (idx > 0 && tokens[idx].kind == TokenKind.NL) {
idx--
}
(tokens[idx], idx)
}

func next(): Token {
tokens[current_ + 1]
}
Expand Down Expand Up @@ -132,7 +140,7 @@ class ParserHelper {
}
}

func synchronizeBlockParenthesis(start: Token, current: Int64): ?FinalError {
func synchronizeBlockParenthesis(start: Token, current: Int64): Unit {
var counter: Int64 = 1
if (!check(TokenKind.LCURL) && !check(TokenKind.COMMA)) {
do {
Expand All @@ -148,35 +156,31 @@ class ParserHelper {
reporter.pop()
}
if (isAtEnd()) {
errorFinal(start, "unclosed delimiter: '('", current)
reporter.unclosedDelimiterError(peek(), start, current, current_)
} else if (check(TokenKind.LCURL) && counter > 0) {
error(start, "unclosed delimiter: '('", current)
reporter.unclosedDelimiterError(peek(), start, current, current_)
} else if (check(TokenKind.COMMA)) {
error(tokens[this.current - 1], "Malformed parameter", this.current - 1)
} else {
None
}
}

func synchronizeBlockRightCurl(start: Token, current: Int64): ?FinalError {
func trySynchronizeBlockRightCurl(): Bool {
let beforeSynchronization = current_
var counter: Int64 = 1
if (!check(TokenKind.RCURL)) {
do {
if (peek().kind == TokenKind.LCURL) {
counter++
}
if (peek().kind == TokenKind.RCURL) {
counter--
}
advance()
} while (!isAtEnd() && counter > 0)
}
do {
if (peek().kind == TokenKind.LCURL) {
counter++
}
if (peek().kind == TokenKind.RCURL) {
counter--
}
advance()
} while (!isAtEnd() && counter > 0)
if (isAtEnd()) {
errorFinal(start, "unclosed delimiter: '{'", current)
} else if (counter == 0) {
return None
current_ = beforeSynchronization
false
} else {
error(start, "unclosed delimiter: '{'", current)
true
}
}

Expand Down Expand Up @@ -232,9 +236,20 @@ class ParserHelper {
ConsumeError()
}

// func unclosedDelimiterError(currentToken: Token, startToken: Token, index: Int64) {
// reporter.unclosedDelimiterError(currentToken, startToken, index)
// }
func unclosedDelimiterError(currentToken: Token, startToken: Token, startIndex: Int64, currentIndex: Int64) {
reporter.pop()
reporter.unclosedDelimiterError(currentToken, startToken, startIndex, currentIndex)
if (previous().kind == TokenKind.NL) {
retreat()
}
}

func trySynchronizeBlockRightCurlOrError(currentToken: Token, startToken: Token, startIndex: Int64,
currentIndex: Int64) {
if (!trySynchronizeBlockRightCurl()) {
unclosedDelimiterError(currentToken, startToken, startIndex, currentIndex)
}
}

func logErrors() {
reporter.logErrors()
Expand Down
16 changes: 12 additions & 4 deletions src/parser/Reporter.cj
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@ public class Reporter {
}

func unclosedDelimiterError(currentToken: Token, startToken: Token, startIndex: Int64, currentIndex: Int64) {
pop()
pop()
errors.add(
ErrorUnclosedDelimiter(currentToken.pos.line, startIndex, startToken.value, getSuffixPrefix(startIndex),
currentIndex, currentToken.value, getSuffixPrefix(currentIndex)))
ErrorUnclosedDelimiter(startToken.pos.line, startIndex, startToken.value, getSuffixPrefix(startIndex),
currentToken.pos.line, currentIndex, currentToken.value, getSuffixPrefix(currentIndex),
getEndToken(startToken.kind)))
}

public static func redify(str: String) {
Expand All @@ -89,3 +88,12 @@ Failed to parse program.
}
}
}

private func getEndToken(startKind: TokenKind): String {
match (startKind) {
case TokenKind.LPAREN => ")"
case TokenKind.LCURL => "}"
case TokenKind.LSQUARE => "]"
case _ => throw Exception("Invalid start token kind for delimiter")
}
}