262 lines
4.9 KiB
Go
262 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"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 BlockStatement struct {
|
|
Block Block
|
|
}
|
|
|
|
type ReturnStatement struct {
|
|
Value *Expression
|
|
}
|
|
|
|
type DeclareLocalVariableStatement struct {
|
|
Variable string
|
|
Initializer Expression
|
|
}
|
|
|
|
type ExpressionType uint32
|
|
|
|
const (
|
|
Expression_Assignment ExpressionType = iota
|
|
Expression_Literal
|
|
Expression_VariableReference
|
|
Expression_Arithmetic
|
|
)
|
|
|
|
type Expression struct {
|
|
Type ExpressionType
|
|
Value any
|
|
}
|
|
|
|
type AssignmentExpression struct {
|
|
Variable string
|
|
Value Expression
|
|
}
|
|
|
|
type LiteralExpression struct {
|
|
Type PrimitiveType
|
|
Value any
|
|
}
|
|
|
|
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 Block struct {
|
|
Statements []Statement
|
|
}
|
|
|
|
type ParsedFunction struct {
|
|
Parameters []Type
|
|
ReturnType Type
|
|
Body Block
|
|
}
|
|
|
|
type Import struct {
|
|
Import string
|
|
}
|
|
|
|
type ParsedFile struct {
|
|
Imports []Import
|
|
Functions []ParsedFunction
|
|
}
|
|
|
|
func expectSeparator(tokens []LexToken, separators ...Separator) (Separator, []LexToken, error) {
|
|
var separatorNames []string
|
|
for _, sep := range separators {
|
|
separatorNames = append(separatorNames, string(Separators[sep]))
|
|
}
|
|
|
|
if len(tokens) == 0 {
|
|
return InvalidValue, nil, errors.New("expected one of " + strings.Join(separatorNames, " "))
|
|
}
|
|
|
|
separator := tokens[0]
|
|
if separator.Type != Type_Separator || !slices.Contains(separators, separator.Value.(Separator)) {
|
|
return InvalidValue, nil, errors.New("expected one of " + strings.Join(separatorNames, " "))
|
|
}
|
|
|
|
return separator.Value.(Separator), tokens[1:], nil
|
|
}
|
|
|
|
func parseImport(tokens []LexToken) (*Import, []LexToken, error) {
|
|
var err error
|
|
|
|
if len(tokens) < 3 {
|
|
return nil, nil, errors.New("incomplete import")
|
|
}
|
|
|
|
// tokens[0] == import keyword
|
|
|
|
identifier := tokens[1]
|
|
if identifier.Type != Type_Identifier {
|
|
return nil, nil, errors.New("expected identifier")
|
|
}
|
|
|
|
_, tokens, err = expectSeparator(tokens[2:], Separator_Semicolon)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return &Import{Import: identifier.Value.(string)}, tokens, nil
|
|
}
|
|
|
|
func parseType(tokens []LexToken) (*Type, []LexToken, error) {
|
|
var err error
|
|
|
|
if len(tokens) == 0 {
|
|
return nil, nil, errors.New("expected type")
|
|
}
|
|
|
|
tok := tokens[0]
|
|
if tok.Type == Type_Keyword && tok.Value.(Keyword) == Keyword_Void {
|
|
return &Type{Type: Type_Void, Value: nil}, tokens[1:], nil
|
|
}
|
|
|
|
if tok.Type == Type_Separator && tok.Value.(Separator) == Separator_OpenParen {
|
|
// Tuple type
|
|
var types []Type
|
|
tokens = tokens[1:]
|
|
for {
|
|
var parsedType *Type
|
|
parsedType, tokens, err = parseType(tokens)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
types = append(types, *parsedType)
|
|
|
|
var sep Separator
|
|
sep, tokens, err = expectSeparator(tokens, Separator_Comma, Separator_CloseParen)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if sep == Separator_CloseParen {
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(types) == 0 {
|
|
return nil, nil, errors.New("empty tuple")
|
|
}
|
|
|
|
return &Type{Type: Type_Tuple, Value: TupleType{Types: types}}, tokens, nil
|
|
}
|
|
|
|
if tok.Type == Type_Identifier {
|
|
return &Type{Type: Type_Named, Value: tok.Value}, tokens[1:], nil
|
|
}
|
|
|
|
return nil, nil, errors.New("expected type")
|
|
}
|
|
|
|
func parseFunction(tokens []LexToken) (*ParsedFunction, []LexToken, error) {
|
|
var err error
|
|
|
|
var parameters []Type
|
|
var returnType *Type
|
|
var body Block
|
|
|
|
returnType, tokens, err = parseType(tokens)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// TODO: function name, parameters, body
|
|
|
|
return &ParsedFunction{Parameters: parameters, ReturnType: *returnType, Body: body}, tokens, nil
|
|
}
|
|
|
|
func parser(tokens []LexToken) (*ParsedFile, error) {
|
|
var err error
|
|
|
|
var functions []ParsedFunction
|
|
var imports []Import
|
|
|
|
for len(tokens) > 0 {
|
|
if tokens[0].Type == Type_Keyword && tokens[0].Value.(Keyword) == Keyword_Import {
|
|
var parsedImport *Import
|
|
parsedImport, tokens, err = parseImport(tokens)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
imports = append(imports, *parsedImport)
|
|
continue
|
|
}
|
|
|
|
var parsedFunction *ParsedFunction
|
|
parsedFunction, tokens, err = parseFunction(tokens)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
functions = append(functions, *parsedFunction)
|
|
}
|
|
|
|
return &ParsedFile{Imports: imports, Functions: functions}, nil
|
|
}
|