// SPDX-FileCopyrightText: 2024 Himbeer // // SPDX-License-Identifier: GPL-3.0-or-later package main import ( "errors" ) var unexpectedEOF = errors.New("unexpected EOF") func (t *tokens) consumeToken() (token, bool) { if t.index >= len(t.toks) { tok, ok := <-t.src if !ok { return token{}, false } t.toks = append(t.toks, tok) } tok := t.toks[t.index] t.index++ return tok, true } func (t *tokens) unreadToken() { t.unreadTokens(1) } func (t *tokens) unreadTokens(n int) { t.index -= n } func (t *tokens) current() token { return t.toks[t.index-1] } func (t *tokens) eof() bool { _, ok := t.consumeToken() if ok { t.unreadToken() } return !ok } func (t *tokens) match(kind tokenKind) (bool, error) { tok, ok := t.consumeToken() if !ok { return false, unexpectedEOF } return tok.kind == kind, nil } func (t *tokens) mustMatch(kind tokenKind) error { ok, err := t.match(kind) if err != nil { return err } if !ok { return expected{want: kind, got: t.current()} } return nil } func parse(toks *tokens, root chan<- *rootExpr, errs chan<- error) { defer close(root) expr, err := parseRoot(toks, errs) if err != nil { errs <- err } else { root <- expr } } func parseRoot(toks *tokens, errs chan<- error) (*rootExpr, error) { toplevels := make([]expression, 0) for !toks.eof() { toplevel, err := parseToplevel(toks) if err != nil { return nil, err } if toplevel != nil { toplevels = append(toplevels, toplevel) } } return &rootExpr{ toplevels: toplevels, }, nil } func parseToplevel(toks *tokens) (expression, error) { function, err := parseFunction(toks) if err != nil { return nil, err } if function != nil { return function, nil } externFunc, err := parseExternFunc(toks) if err != nil { return nil, err } if externFunc != nil { return externFunc, nil } return nil, expectedToplevel{got: toks.current()} } func parseFunction(toks *tokens) (*functionExpr, error) { link := exportLinkage exported, err := toks.match(export) if err != nil { return nil, err } line := toks.current().line if !exported { toks.unreadToken() link = defaultLinkage } ok, err := toks.match(function) if err != nil { return nil, err } if !ok { toks.unreadToken() if exported { toks.unreadToken() } return nil, nil } nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { return nil, expected{want: identifier, got: nameTok} } if err := toks.mustMatch(lparen); err != nil { return nil, err } var params *paramExpr ok, err = toks.match(rparen) if err != nil { return nil, err } if !ok { toks.unreadToken() params, err = parseParam(toks) if err != nil { return nil, err } if err := toks.mustMatch(rparen); err != nil { return nil, err } } returnTypeTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if returnTypeTok.kind != identifier { return nil, expected{want: identifier, got: returnTypeTok} } blk, err := parseBlock(toks) if err != nil { return nil, err } return &functionExpr{ link: link, name: nameTok.value, params: params, returnType: returnTypeTok.value, blk: blk, ln: line, }, nil } func parseParam(toks *tokens) (*paramExpr, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { return nil, expected{want: identifier, got: nameTok} } typeTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if typeTok.kind != identifier { return nil, expected{want: identifier, got: typeTok} } var next *paramExpr ok, err := toks.match(comma) if err != nil { return nil, err } if ok { next, err = parseParam(toks) if err != nil { return nil, err } } else { toks.unreadToken() } return ¶mExpr{ name: nameTok.value, typ: typeTok.value, next: next, }, nil } func parseExternFunc(toks *tokens) (*externFuncExpr, error) { ok, err := toks.match(extern) if err != nil { return nil, err } if !ok { toks.unreadToken() return nil, nil } line := toks.current().line if err := toks.mustMatch(function); err != nil { return nil, err } nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { return nil, expected{want: identifier, got: nameTok} } if err := toks.mustMatch(lparen); err != nil { return nil, err } var params *signatureExpr ok, err = toks.match(rparen) if err != nil { return nil, err } if !ok { toks.unreadToken() params, err = parseSignature(toks) if err != nil { return nil, err } if err := toks.mustMatch(rparen); err != nil { return nil, err } } returnTypeTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if returnTypeTok.kind != identifier { return nil, expected{want: identifier, got: returnTypeTok} } if err := toks.mustMatch(semicolon); err != nil { return nil, err } return &externFuncExpr{ name: nameTok.value, params: params, returnType: returnTypeTok.value, ln: line, }, nil } func parseSignature(toks *tokens) (*signatureExpr, error) { typeTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if typeTok.kind != identifier { return nil, expected{want: identifier, got: typeTok} } var next *signatureExpr ok, err := toks.match(comma) if err != nil { return nil, err } if ok { next, err = parseSignature(toks) if err != nil { return nil, err } } else { toks.unreadToken() } return &signatureExpr{ typ: typeTok.value, next: next, ln: typeTok.line, }, nil } func parseBlock(toks *tokens) (*blockExpr, error) { if err := toks.mustMatch(lbrace); err != nil { return nil, err } var expectStmt bool stmts := make([]statementExpr, 0) stmt, err := parseStatement(toks) if err != nil { return nil, err } if stmt != nil { stmts = append(stmts, stmt) } else { expectStmt = true } for stmt != nil { stmt, err = parseStatement(toks) if err != nil { return nil, err } if stmt == nil { break } stmts = append(stmts, stmt) } if err := toks.mustMatch(rbrace); err != nil { if expectStmt { return nil, expectedStatement{got: toks.current()} } return nil, err } return &blockExpr{stmts: stmts}, nil } func parseStatement(toks *tokens) (statementExpr, error) { stmt, err := parseStatementContents(toks) if err != nil { return nil, err } if stmt == nil { return nil, nil } if err := toks.mustMatch(semicolon); err != nil { return nil, err } return stmt, nil } func parseStatementContents(toks *tokens) (statementExpr, error) { stmt, err := parseReturnStmt(toks) if err != nil { return nil, err } if stmt != nil { return stmt, nil } decl, err := parseDeclaration(toks) if err != nil { return nil, err } if decl != nil { return decl, nil } assignment, err := parseAssignment(toks) if err != nil { return nil, err } if assignment != nil { return assignment, nil } return nil, nil } func parseReturnStmt(toks *tokens) (*returnStmt, error) { ok, err := toks.match(ret) if err != nil { return nil, err } if !ok { toks.unreadToken() return nil, nil } expr, err := parseExpression(toks) if err != nil { return nil, err } return &returnStmt{value: expr, ln: toks.current().line}, nil } func parseDeclaration(toks *tokens) (declStmt, error) { constant, err := parseConstStmt(toks) if err != nil { return nil, err } if constant != nil { return constant, nil } mutable, err := parseMutStmt(toks) if err != nil { return nil, err } if mutable != nil { return mutable, nil } return nil, nil } func parseAssignment(toks *tokens) (assignmentStmt, error) { assignment, err := parseAssignStmt(toks) if err != nil { return nil, err } if assignment != nil { return assignment, nil } addAssign, err := parseAddAssignStmt(toks) if err != nil { return nil, err } if addAssign != nil { return addAssign, nil } subAssign, err := parseSubAssignStmt(toks) if err != nil { return nil, err } if subAssign != nil { return subAssign, nil } mulAssign, err := parseMulAssignStmt(toks) if err != nil { return nil, err } if mulAssign != nil { return mulAssign, nil } divAssign, err := parseDivAssignStmt(toks) if err != nil { return nil, err } if divAssign != nil { return divAssign, nil } remAssign, err := parseRemAssignStmt(toks) if err != nil { return nil, err } if remAssign != nil { return remAssign, nil } return nil, nil } func parseConstStmt(toks *tokens) (*constStmt, error) { ok, err := toks.match(constKeyword) if err != nil { return nil, err } if !ok { toks.unreadToken() return nil, nil } line := toks.current().line nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if err := toks.mustMatch(equals); err != nil { return nil, err } expr, err := parseExpression(toks) if err != nil { return nil, err } return &constStmt{name: nameTok.value, initial: expr, ln: line}, nil } func parseMutStmt(toks *tokens) (*mutStmt, error) { ok, err := toks.match(mut) if err != nil { return nil, err } if !ok { toks.unreadToken() return nil, nil } line := toks.current().line nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if err := toks.mustMatch(equals); err != nil { return nil, err } expr, err := parseExpression(toks) if err != nil { return nil, err } return &mutStmt{name: nameTok.value, initial: expr, ln: line}, nil } func parseAssignStmt(toks *tokens) (*assignStmt, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } ok, err := toks.match(equals) if err != nil { return nil, err } if !ok { toks.unreadTokens(2) return nil, nil } expr, err := parseExpression(toks) if err != nil { return nil, err } return &assignStmt{ name: nameTok.value, value: expr, ln: nameTok.line, }, nil } func parseAddAssignStmt(toks *tokens) (*addAssignStmt, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } ok, err := toks.match(plusEquals) if err != nil { return nil, err } if !ok { toks.unreadTokens(2) return nil, nil } expr, err := parseExpression(toks) if err != nil { return nil, err } return &addAssignStmt{ name: nameTok.value, value: expr, ln: nameTok.line, }, nil } func parseSubAssignStmt(toks *tokens) (*subAssignStmt, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } ok, err := toks.match(minusEquals) if err != nil { return nil, err } if !ok { toks.unreadTokens(2) return nil, nil } expr, err := parseExpression(toks) if err != nil { return nil, err } return &subAssignStmt{ name: nameTok.value, value: expr, ln: nameTok.line, }, nil } func parseMulAssignStmt(toks *tokens) (*mulAssignStmt, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } ok, err := toks.match(starEquals) if err != nil { return nil, err } if !ok { toks.unreadTokens(2) return nil, nil } expr, err := parseExpression(toks) if err != nil { return nil, err } return &mulAssignStmt{ name: nameTok.value, value: expr, ln: nameTok.line, }, nil } func parseDivAssignStmt(toks *tokens) (*divAssignStmt, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } ok, err := toks.match(slashEquals) if err != nil { return nil, err } if !ok { toks.unreadTokens(2) return nil, nil } expr, err := parseExpression(toks) if err != nil { return nil, err } return &divAssignStmt{ name: nameTok.value, value: expr, ln: nameTok.line, }, nil } func parseRemAssignStmt(toks *tokens) (*remAssignStmt, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } ok, err := toks.match(percentageEquals) if err != nil { return nil, err } if !ok { toks.unreadTokens(2) return nil, nil } expr, err := parseExpression(toks) if err != nil { return nil, err } return &remAssignStmt{ name: nameTok.value, value: expr, ln: nameTok.line, }, nil } func parseExpression(toks *tokens) (exprExpr, error) { return parseEquality(toks) } func parseEquality(toks *tokens) (*equalityExpr, error) { lhs, err := parseComparison(toks) if err != nil { return nil, err } line := toks.current().line rhss := make([]*equalityRhs, 0) for { rhs, err := parseEqualityRhs(toks) if err != nil { return nil, err } if rhs == nil { break } rhss = append(rhss, rhs) } return &equalityExpr{lhs: lhs, rhs: rhss, ln: line}, nil } func parseEqualityRhs(toks *tokens) (*equalityRhs, error) { eq, err := toks.match(doubleEquals) if err != nil { return nil, err } if !eq { toks.unreadToken() } neq, err := toks.match(bangEquals) if err != nil { return nil, err } if !neq { toks.unreadToken() return nil, nil } value, err := parseComparison(toks) if err != nil { return nil, err } var op equalityOp if eq { op = equalTo } else if neq { op = notEqualTo } return &equalityRhs{op: op, value: value}, nil } func parseComparison(toks *tokens) (*comparisonExpr, error) { lhs, err := parseTerm(toks) if err != nil { return nil, err } line := toks.current().line rhs, err := parseComparisonRhs(toks) if err != nil { return nil, err } return &comparisonExpr{lhs: lhs, rhs: rhs, ln: line}, nil } func parseComparisonRhs(toks *tokens) (*comparisonRhs, error) { lt, err := toks.match(langle) if err != nil { return nil, err } if !lt { toks.unreadToken() } le, err := toks.match(langleEquals) if err != nil { return nil, err } if !le { toks.unreadToken() } gt, err := toks.match(rangle) if err != nil { return nil, err } if !gt { toks.unreadToken() } ge, err := toks.match(rangleEquals) if err != nil { return nil, err } if !ge { toks.unreadToken() return nil, nil } value, err := parseTerm(toks) if err != nil { return nil, err } var op comparisonOp if lt { op = lessThan } else if le { op = lessThanOrEqualTo } else if gt { op = greaterThan } else if ge { op = greaterThanOrEqualTo } return &comparisonRhs{op: op, value: value}, nil } func parseTerm(toks *tokens) (*termExpr, error) { lhs, err := parseNumeral(toks) if err != nil { return nil, err } line := toks.current().line rhss := make([]*termRhs, 0) for { rhs, err := parseTermRhs(toks) if err != nil { return nil, err } if rhs == nil { break } rhss = append(rhss, rhs) } return &termExpr{lhs: lhs, rhs: rhss, ln: line}, nil } func parseTermRhs(toks *tokens) (*termRhs, error) { lsh, err := toks.match(doubleLangle) if err != nil { return nil, err } if !lsh { toks.unreadToken() } rsh, err := toks.match(doubleRangle) if err != nil { return nil, err } if !rsh { toks.unreadToken() return nil, nil } value, err := parseNumeral(toks) if err != nil { return nil, err } var op shiftOp if lsh { op = shiftLeft } else if rsh { op = shiftRight } return &termRhs{op: op, value: value}, nil } func parseNumeral(toks *tokens) (*numeralExpr, error) { lhs, err := parseFactor(toks) if err != nil { return nil, err } line := toks.current().line rhss := make([]*numeralRhs, 0) for { rhs, err := parseNumeralRhs(toks) if err != nil { return nil, err } if rhs == nil { break } rhss = append(rhss, rhs) } return &numeralExpr{lhs: lhs, rhs: rhss, ln: line}, nil } func parseNumeralRhs(toks *tokens) (*numeralRhs, error) { doAdd, err := toks.match(plus) if err != nil { return nil, err } var doSubtract bool if !doAdd { toks.unreadToken() doSubtract, err = toks.match(minus) if err != nil { return nil, err } if !doSubtract { toks.unreadToken() return nil, nil } } value, err := parseFactor(toks) if err != nil { return nil, err } var op addSubOp if doAdd { op = add } else if doSubtract { op = subtract } return &numeralRhs{op: op, value: value}, nil } func parseFactor(toks *tokens) (*factorExpr, error) { lhs, err := parseUnary(toks) if err != nil { return nil, err } line := toks.current().line rhss := make([]*factorRhs, 0) for { rhs, err := parseFactorRhs(toks) if err != nil { return nil, err } if rhs == nil { break } rhss = append(rhss, rhs) } return &factorExpr{lhs: lhs, rhs: rhss, ln: line}, nil } func parseFactorRhs(toks *tokens) (*factorRhs, error) { mul, err := toks.match(star) if err != nil { return nil, err } var div bool if !mul { toks.unreadToken() div, err = toks.match(slash) if err != nil { return nil, err } } var rem bool if !mul && !div { toks.unreadToken() rem, err = toks.match(percentage) if err != nil { return nil, err } if !rem { toks.unreadToken() return nil, nil } } value, err := parseUnary(toks) if err != nil { return nil, err } var op mulDivOp if mul { op = multiply } else if div { op = divide } else if rem { op = remainder } return &factorRhs{op: op, value: value}, nil } func parseUnary(toks *tokens) (*unaryExpr, error) { neg, err := toks.match(minus) if err != nil { return nil, err } line := toks.current().line if !neg { toks.unreadToken() } invLog, err := toks.match(bang) if err != nil { return nil, err } if !invLog { toks.unreadToken() } invBits, err := toks.match(tilde) if err != nil { return nil, err } if !invBits { toks.unreadToken() } value, err := parsePrimary(toks) if err != nil { return nil, err } var op unaryOp if neg { op = negate } else if invLog { op = invertLogical } else if invBits { op = invertBits } return &unaryExpr{op: op, value: value, ln: line}, nil } func parsePrimary(toks *tokens) (primaryExpr, error) { grp, err := parseGrouping(toks) if err != nil { return nil, err } if grp != nil { return grp, nil } lit, err := parseLiteral(toks) if err != nil { return nil, err } if lit != nil { return lit, nil } call, err := parseCall(toks) if err != nil { return nil, err } if call != nil { return call, nil } variable, err := parseVariable(toks) if err != nil { return nil, err } if variable != nil { return variable, nil } return nil, expectedPrimary{got: toks.current()} } func parseGrouping(toks *tokens) (*groupingExpr, error) { ok, err := toks.match(lparen) if err != nil { return nil, err } if !ok { toks.unreadToken() return nil, nil } line := toks.current().line expr, err := parseExpression(toks) if err != nil { return nil, err } if err := toks.mustMatch(rparen); err != nil { return nil, err } return &groupingExpr{inner: expr, ln: line}, nil } func parseLiteral(toks *tokens) (literalExpr, error) { s, err := parseString(toks) if err != nil { return nil, err } if s != nil { return s, nil } num, err := parseNumber(toks) if err != nil { return nil, err } if num != nil { return num, nil } return nil, nil } func parseString(toks *tokens) (*stringExpr, error) { tok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } toks.unreadToken() segments := make([]string, 0) for { segment, err := parseStringSegment(toks) if err != nil { return nil, err } if segment == nil { break } segments = append(segments, *segment) } if len(segments) == 0 { return nil, nil } return &stringExpr{segments: segments, ln: tok.line}, nil } func parseStringSegment(toks *tokens) (*string, error) { tok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if tok.kind != str { toks.unreadToken() return nil, nil } return &tok.value, nil } func parseNumber(toks *tokens) (*numberExpr, error) { numTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if numTok.kind != number { toks.unreadToken() return nil, nil } typeTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if typeTok.kind != identifier { return nil, expected{want: identifier, got: typeTok} } return &numberExpr{typ: typeTok.value, s: numTok.value, ln: numTok.line}, nil } func parseCall(toks *tokens) (*callExpr, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } ok, err := toks.match(lparen) if err != nil { return nil, err } if !ok { toks.unreadTokens(2) return nil, nil } numArgs := 0 args, err := parseArg(toks, &numArgs) if err != nil { return nil, err } if err := toks.mustMatch(rparen); err != nil { return nil, err } return &callExpr{ funcName: nameTok.value, args: args, numArgs: numArgs, ln: nameTok.line, }, nil } func parseArg(toks *tokens, numTotal *int) (*argument, error) { ok, err := toks.match(rparen) if err != nil { return nil, err } toks.unreadToken() if ok { return nil, nil } arg, err := parseExpression(toks) if err != nil { return nil, err } ok, err = toks.match(comma) if err != nil { return nil, err } if !ok { toks.unreadToken() *numTotal++ return &argument{value: arg}, nil } next, err := parseArg(toks, numTotal) if err != nil { return nil, err } *numTotal++ return &argument{value: arg, next: next}, nil } func parseVariable(toks *tokens) (*varExpr, error) { nameTok, ok := toks.consumeToken() if !ok { return nil, unexpectedEOF } if nameTok.kind != identifier { toks.unreadToken() return nil, nil } return &varExpr{name: nameTok.value, ln: nameTok.line}, nil }