elysium/parser.go
2024-03-17 19:55:28 +01:00

782 lines
15 KiB
Go

package main
import (
"slices"
"strings"
)
type TypeType uint32
const (
Type_Primitive TypeType = iota
Type_Void
Type_Named
Type_Array
Type_Tuple
)
type Type struct {
Type TypeType
Value any
}
type NamedType struct {
TypeName string
}
type ArrayType struct {
ElementType Type
}
type TupleType struct {
Types []Type
}
type StatementType uint32
const (
Statement_Expression StatementType = iota
Statement_Block
Statement_Return
Statement_DeclareLocalVariable
)
type Statement struct {
Type StatementType
Value any
}
type ExpressionStatement struct {
Expression Expression
}
type BlockStatement struct {
Block Block
}
type ReturnStatement struct {
Value *Expression
}
type DeclareLocalVariableStatement struct {
Variable string
VariableType Type
Initializer *Expression
}
type ExpressionType uint32
const (
Expression_Assignment ExpressionType = iota
Expression_Literal
Expression_VariableReference
Expression_Arithmetic
Expression_Tuple
)
type Expression struct {
Type ExpressionType
Value any
ValueType Type
}
type AssignmentExpression struct {
Variable string
Value Expression
}
type LiteralExpression struct {
Literal Literal
}
type VariableReferenceExpression struct {
Variable string
}
type ArithmeticOperation uint32
const (
Arithmetic_Add ArithmeticOperation = iota
Arithmetic_Sub
Arithmetic_Mul
Arithmetic_Div
Arithmetic_Mod
)
type ArithmeticExpression struct {
Operation ArithmeticOperation
Left Expression
Right Expression
}
type TupleExpression struct {
Members []Expression
}
type Local struct {
Name string
Type Type
}
type Block struct {
Statements []Statement
Locals map[string]Local
}
type ParsedParameter struct {
Name string
Type Type
}
type ParsedFunction struct {
Name string
Parameters []ParsedParameter
ReturnType Type
Body Block
}
type Import struct {
Import string
}
type ParsedFile struct {
Imports []Import
Functions []ParsedFunction
}
type Parser struct {
Tokens []LexToken
Position uint64
}
func (p Parser) copy() Parser {
return p
}
func (p *Parser) error(message string) CompilerError {
return CompilerError{Position: p.Position, Message: message}
}
func (p *Parser) peekToken() *LexToken {
if len(p.Tokens) == 0 {
return nil
}
return &p.Tokens[0]
}
func (p *Parser) nextToken() *LexToken {
if len(p.Tokens) == 0 {
return nil
}
token := p.Tokens[0]
p.Tokens = p.Tokens[1:]
p.Position = token.Position
return &token
}
func (p *Parser) trySeparator(separators ...Separator) (*Separator, error) {
pCopy := p.copy()
separator := pCopy.nextToken()
if separator == nil || separator.Type != Type_Separator || !slices.Contains(separators, separator.Value.(Separator)) {
return nil, nil
}
*p = pCopy
sep := separator.Value.(Separator)
return &sep, nil
}
func (p *Parser) expectSeparator(separators ...Separator) (Separator, error) {
sep, err := p.trySeparator(separators...)
if err != nil {
return InvalidValue, err
}
if sep == nil {
var separatorNames []string
for _, sep := range separators {
separatorNames = append(separatorNames, string(Separators[sep]))
}
return InvalidValue, p.error("expected one of " + strings.Join(separatorNames, " "))
}
return *sep, nil
}
func (p *Parser) tryOperator(operators ...Operator) (*Operator, error) {
pCopy := p.copy()
operator := pCopy.nextToken()
if operator == nil || operator.Type != Type_Operator || !slices.Contains(operators, operator.Value.(Operator)) {
return nil, nil
}
*p = pCopy
sep := operator.Value.(Operator)
return &sep, nil
}
func (p *Parser) expectIdentifier() (string, error) {
identifier := p.nextToken()
if identifier == nil || identifier.Type != Type_Identifier {
return "", p.error("expected identifier")
}
return identifier.Value.(string), nil
}
func (p *Parser) expectImport() (*Import, error) {
var err error
importToken := p.nextToken()
if importToken == nil || importToken.Type != Type_Keyword || importToken.Value.(Keyword) != Keyword_Import {
return nil, p.error("expected import")
}
identifier := p.nextToken()
if identifier == nil || identifier.Type != Type_Identifier {
return nil, p.error("expected identifier")
}
_, err = p.expectSeparator(Separator_Semicolon)
if err != nil {
return nil, err
}
return &Import{Import: identifier.Value.(string)}, nil
}
func (p *Parser) tryType() (*Type, error) {
pCopy := p.copy()
tok := pCopy.nextToken()
if tok == nil {
return nil, nil
}
if tok.Type == Type_Keyword && tok.Value.(Keyword) == Keyword_Void {
*p = pCopy
return &Type{Type: Type_Void, Value: nil}, nil
}
if tok.Type == Type_Identifier {
// TODO: array type
index := slices.Index(PRIMITIVE_TYPE_NAMES, tok.Value.(string))
if index != -1 {
*p = pCopy
return &Type{Type: Type_Primitive, Value: PrimitiveType(index)}, nil
}
*p = pCopy
return &Type{Type: Type_Named, Value: tok.Value}, nil
}
return nil, nil
}
func (p *Parser) expectType() (*Type, error) {
t, err := p.tryType()
if err != nil {
return nil, err
}
if t != nil {
return t, nil
}
return nil, p.error("expected type")
}
func (p *Parser) expectTypeOrTupleType() (*Type, error) {
var err error
tok := p.peekToken()
if tok == nil {
return nil, p.error("expected type or tuple type")
}
if tok.Type == Type_Separator && tok.Value.(Separator) == Separator_OpenParen {
// Tuple type
p.nextToken()
var types []Type
for {
parsedType, err := p.expectType()
if err != nil {
return nil, err
}
types = append(types, *parsedType)
var sep Separator
sep, err = p.expectSeparator(Separator_Comma, Separator_CloseParen)
if err != nil {
return nil, err
}
if sep == Separator_CloseParen {
break
}
}
if len(types) == 0 {
return nil, p.error("empty tuple")
}
return &Type{Type: Type_Tuple, Value: TupleType{Types: types}}, nil
}
t, err := p.tryType()
if err != nil {
return nil, err
}
if t != nil {
return t, nil
}
return nil, p.error("expected type or tuple type")
}
func (p *Parser) tryParanthesizedExpression() (*Expression, error) {
pCopy := p.copy()
token := pCopy.nextToken()
if token == nil || token.Type != Type_Separator || token.Value.(Separator) != Separator_OpenParen {
return nil, nil
}
expr, err := pCopy.tryExpression()
if err != nil {
*p = pCopy
return nil, err
}
if expr == nil {
return nil, nil
}
// Assuming we're in a paranthesized statement
_, err = pCopy.expectSeparator(Separator_CloseParen)
if err != nil {
*p = pCopy
return nil, err
}
*p = pCopy
return expr, nil
}
func (p *Parser) tryUnaryExpression() (*Expression, error) {
pCopy := p.copy()
token := pCopy.peekToken()
if token == nil {
return nil, nil
}
if token.Type == Type_Separator && token.Value.(Separator) == Separator_OpenParen {
// TODO: cast
paren, err := pCopy.tryParanthesizedExpression()
if err != nil {
*p = pCopy
return nil, err
}
if paren != nil {
*p = pCopy
return paren, nil
}
}
if token.Type == Type_Literal {
pCopy.nextToken()
*p = pCopy
return &Expression{Type: Expression_Literal, Value: LiteralExpression{Literal: token.Value.(Literal)}}, nil
}
if token.Type == Type_Keyword {
keyword := token.Value.(Keyword)
if keyword == Keyword_True || keyword == KeyWord_False {
pCopy.nextToken()
*p = pCopy
return &Expression{Type: Expression_Literal, Value: LiteralExpression{Literal: Literal{Type: Literal_Boolean, Primitive: Primitive_Bool, Value: keyword == Keyword_True}}}, nil
}
}
if token.Type == Type_Identifier {
pCopy.nextToken()
*p = pCopy
return &Expression{Type: Expression_VariableReference, Value: VariableReferenceExpression{Variable: token.Value.(string)}}, nil
}
return nil, nil
}
func (p *Parser) tryMultiplicativeExpression() (*Expression, error) {
left, err := p.tryUnaryExpression()
if err != nil {
return nil, err
}
op, err := p.tryOperator(Operator_Multiply, Operator_Divide, Operator_Modulo)
if err != nil {
return nil, err
}
if op == nil {
return left, nil
}
right, err := p.tryUnaryExpression()
if err != nil {
return nil, err
}
if right == nil {
return nil, p.error("expected expression")
}
var operation ArithmeticOperation
switch *op {
case Operator_Multiply:
operation = Arithmetic_Mul
case Operator_Divide:
operation = Arithmetic_Div
case Operator_Modulo:
fallthrough
default:
operation = Arithmetic_Mod
}
if *op == Operator_Plus {
operation = Arithmetic_Add
} else {
operation = Arithmetic_Sub
}
return &Expression{Type: Expression_Arithmetic, Value: ArithmeticExpression{Operation: operation, Left: *left, Right: *right}}, nil
}
func (p *Parser) tryAdditiveExpression() (*Expression, error) {
left, err := p.tryMultiplicativeExpression()
if err != nil {
return nil, err
}
op, err := p.tryOperator(Operator_Plus, Operator_Minus)
if err != nil {
return nil, err
}
if op == nil {
return left, nil
}
right, err := p.tryMultiplicativeExpression()
if err != nil {
return nil, err
}
if right == nil {
return nil, p.error("expected expression")
}
var operation ArithmeticOperation
if *op == Operator_Plus {
operation = Arithmetic_Add
} else {
operation = Arithmetic_Sub
}
return &Expression{Type: Expression_Arithmetic, Value: ArithmeticExpression{Operation: operation, Left: *left, Right: *right}}, nil
}
func (p *Parser) tryArithmeticExpression() (*Expression, error) {
return p.tryAdditiveExpression()
}
func (p *Parser) tryExpression() (*Expression, error) {
return p.tryArithmeticExpression()
}
func (p *Parser) tryTupleExpression() (*Expression, error) {
pCopy := p.copy()
var members []Expression
for {
expr, err := pCopy.tryExpression()
if err != nil {
return nil, err
}
if expr == nil {
return nil, nil
}
members = append(members, *expr)
token := pCopy.peekToken()
if token == nil || token.Type != Type_Separator || token.Value.(Separator) != Separator_Comma {
if len(members) == 1 {
*p = pCopy
return expr, nil
}
break
}
pCopy.nextToken()
}
*p = pCopy
return &Expression{Type: Expression_Tuple, Value: TupleExpression{Members: members}}, nil
}
func (p *Parser) expectTupleExpression() (*Expression, error) {
return p.expect(p.tryTupleExpression, "expected tuple expression")
}
func (p *Parser) expect(try func() (*Expression, error), message string) (*Expression, error) {
expr, err := try()
if err != nil {
return nil, err
}
if expr == nil {
return nil, p.error(message)
}
return expr, nil
}
func (p *Parser) tryDeclareLocalVariableStatement() (*Statement, error) {
pCopy := p.copy()
variableType, err := pCopy.tryType()
if err != nil {
*p = pCopy
return nil, err
}
if variableType == nil {
return nil, nil
}
name := pCopy.nextToken()
if name == nil || name.Type != Type_Identifier {
return nil, nil
}
variableName := name.Value.(string)
token := pCopy.nextToken()
if token.Type == Type_Separator && token.Value.(Separator) == Separator_Semicolon {
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: nil}}, nil
}
if token.Type != Type_Operator || token.Value.(Operator) != Operator_Equals {
return nil, nil
}
initializer, err := pCopy.expect(pCopy.tryExpression, "expected initializer expression")
if err != nil {
*p = pCopy
return nil, err
}
_, err = pCopy.expectSeparator(Separator_Semicolon)
if err != nil {
*p = pCopy
return nil, err
}
*p = pCopy
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: initializer}}, nil
}
func (p *Parser) expectStatement() (*Statement, error) {
token := p.peekToken()
if token == nil {
return nil, p.error("expected statement")
}
if token.Type == Type_Keyword && token.Value.(Keyword) == Keyword_Return {
p.nextToken()
token = p.peekToken()
if token == nil {
return nil, p.error("expected expression or ;")
}
if token.Type == Type_Separator && token.Value.(Separator) == Separator_Semicolon {
p.nextToken()
return &Statement{Type: Statement_Return, Value: ReturnStatement{Value: nil}}, nil
}
expr, err := p.expectTupleExpression()
if err != nil {
return nil, err
}
_, err = p.expectSeparator(Separator_Semicolon)
if err != nil {
return nil, err
}
return &Statement{Type: Statement_Return, Value: ReturnStatement{Value: expr}}, nil
}
if token.Type == Type_Separator && token.Value.(Separator) == Separator_OpenCurly {
block, err := p.expectBlock()
if err != nil {
return nil, err
}
return &Statement{Type: Statement_Block, Value: BlockStatement{Block: *block}}, nil
}
stmt, err := p.tryDeclareLocalVariableStatement()
if err != nil {
return nil, err
}
if stmt != nil {
return stmt, nil
}
return nil, p.error("expected statement")
}
func (p *Parser) expectBlock() (*Block, error) {
_, err := p.expectSeparator(Separator_OpenCurly)
if err != nil {
return nil, err
}
var statements []Statement
for {
token := p.peekToken()
if token == nil {
return nil, p.error("expected statement or }")
}
if token.Type == Type_Separator && token.Value.(Separator) == Separator_CloseCurly {
p.nextToken()
break
}
stmt, err := p.expectStatement()
if err != nil {
return nil, err
}
statements = append(statements, *stmt)
}
return &Block{Statements: statements}, nil
}
func (p *Parser) expectFunction() (*ParsedFunction, error) {
var err error
var name string
var parameters []ParsedParameter
var returnType *Type
var body *Block
returnType, err = p.expectTypeOrTupleType()
if err != nil {
return nil, err
}
name, err = p.expectIdentifier()
if err != nil {
return nil, err
}
_, err = p.expectSeparator(Separator_OpenParen)
if err != nil {
return nil, err
}
for {
token := p.peekToken()
if token == nil {
return nil, p.error("incomplete function declaration")
}
if token.Type == Type_Separator && token.Value.(Separator) == Separator_CloseParen {
p.nextToken()
break
}
if len(parameters) > 0 {
_, err := p.expectSeparator(Separator_Comma)
if err != nil {
return nil, err
}
}
var paramType *Type
paramType, err = p.expectType()
if err != nil {
return nil, err
}
var paramName string
paramName, err = p.expectIdentifier()
if err != nil {
return nil, err
}
parameters = append(parameters, ParsedParameter{Name: paramName, Type: *paramType})
}
body, err = p.expectBlock()
if err != nil {
return nil, err
}
return &ParsedFunction{Name: name, Parameters: parameters, ReturnType: *returnType, Body: *body}, nil
}
func (p *Parser) parseFile() (*ParsedFile, error) {
var err error
var functions []ParsedFunction
var imports []Import
for {
token := p.peekToken()
if token == nil {
break
}
if token.Type == Type_Keyword && token.Value.(Keyword) == Keyword_Import {
var parsedImport *Import
parsedImport, err = p.expectImport()
if err != nil {
return nil, err
}
imports = append(imports, *parsedImport)
continue
}
var parsedFunction *ParsedFunction
parsedFunction, err = p.expectFunction()
if err != nil {
return nil, err
}
functions = append(functions, *parsedFunction)
}
return &ParsedFile{Imports: imports, Functions: functions}, nil
}