2024-03-11 22:05:36 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"slices"
|
2024-03-13 17:17:09 +01:00
|
|
|
"strconv"
|
2024-03-11 22:05:36 +01:00
|
|
|
"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
|
|
|
|
}
|
|
|
|
|
2024-03-12 22:00:03 +01:00
|
|
|
type ParsedParameter struct {
|
|
|
|
Name string
|
|
|
|
Type Type
|
|
|
|
}
|
|
|
|
|
2024-03-11 22:05:36 +01:00
|
|
|
type ParsedFunction struct {
|
2024-03-12 22:00:03 +01:00
|
|
|
Name string
|
|
|
|
Parameters []ParsedParameter
|
2024-03-11 22:05:36 +01:00
|
|
|
ReturnType Type
|
|
|
|
Body Block
|
|
|
|
}
|
|
|
|
|
|
|
|
type Import struct {
|
|
|
|
Import string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ParsedFile struct {
|
|
|
|
Imports []Import
|
|
|
|
Functions []ParsedFunction
|
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
type Parser struct {
|
2024-03-13 17:17:09 +01:00
|
|
|
Tokens []LexToken
|
|
|
|
Position uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) error(message string) error {
|
|
|
|
return errors.New(message + " (at " + strconv.FormatUint(p.Position, 10) + ")")
|
2024-03-12 22:47:45 +01:00
|
|
|
}
|
2024-03-11 22:05:36 +01:00
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
func (p *Parser) peekToken() *LexToken {
|
2024-03-13 17:17:09 +01:00
|
|
|
if len(p.Tokens) == 0 {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-13 17:17:09 +01:00
|
|
|
return &p.Tokens[0]
|
2024-03-12 22:47:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) nextToken() *LexToken {
|
2024-03-13 17:17:09 +01:00
|
|
|
if len(p.Tokens) == 0 {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-13 17:17:09 +01:00
|
|
|
token := p.Tokens[0]
|
|
|
|
p.Tokens = p.Tokens[1:]
|
|
|
|
p.Position = token.Position
|
2024-03-12 22:47:45 +01:00
|
|
|
return &token
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
func (p *Parser) expectSeparator(separators ...Separator) (Separator, error) {
|
|
|
|
var separatorNames []string
|
|
|
|
for _, sep := range separators {
|
|
|
|
separatorNames = append(separatorNames, string(Separators[sep]))
|
|
|
|
}
|
|
|
|
|
|
|
|
separator := p.nextToken()
|
|
|
|
if separator == nil || separator.Type != Type_Separator || !slices.Contains(separators, separator.Value.(Separator)) {
|
2024-03-13 17:17:09 +01:00
|
|
|
return InvalidValue, p.error("expected one of " + strings.Join(separatorNames, " "))
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
return separator.Value.(Separator), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Parser) expectIdentifier() (string, error) {
|
|
|
|
identifier := p.nextToken()
|
|
|
|
if identifier == nil || identifier.Type != Type_Separator && identifier.Type != Type_Identifier {
|
2024-03-13 17:17:09 +01:00
|
|
|
return "", p.error("expected identifier")
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
return identifier.Value.(string), nil
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
func (p *Parser) expectImport() (*Import, error) {
|
2024-03-11 22:05:36 +01:00
|
|
|
var err error
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
importToken := p.nextToken()
|
|
|
|
if importToken == nil || importToken.Type != Type_Keyword || importToken.Value.(Keyword) != Keyword_Import {
|
2024-03-13 17:17:09 +01:00
|
|
|
return nil, p.error("expected import")
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
identifier := p.nextToken()
|
|
|
|
if identifier == nil || identifier.Type != Type_Identifier {
|
2024-03-13 17:17:09 +01:00
|
|
|
return nil, p.error("expected identifier")
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
_, err = p.expectSeparator(Separator_Semicolon)
|
2024-03-11 22:05:36 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
return &Import{Import: identifier.Value.(string)}, nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
func (p *Parser) expectType() (*Type, error) {
|
2024-03-11 22:05:36 +01:00
|
|
|
var err error
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
tok := p.nextToken()
|
|
|
|
if tok == nil {
|
2024-03-13 17:17:09 +01:00
|
|
|
return nil, p.error("expected type")
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if tok.Type == Type_Keyword && tok.Value.(Keyword) == Keyword_Void {
|
2024-03-12 22:47:45 +01:00
|
|
|
return &Type{Type: Type_Void, Value: nil}, nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if tok.Type == Type_Separator && tok.Value.(Separator) == Separator_OpenParen {
|
|
|
|
// Tuple type
|
|
|
|
var types []Type
|
|
|
|
for {
|
|
|
|
var parsedType *Type
|
2024-03-12 22:47:45 +01:00
|
|
|
parsedType, err = p.expectType()
|
2024-03-11 22:05:36 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
types = append(types, *parsedType)
|
|
|
|
|
|
|
|
var sep Separator
|
2024-03-12 22:47:45 +01:00
|
|
|
sep, err = p.expectSeparator(Separator_Comma, Separator_CloseParen)
|
2024-03-11 22:05:36 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if sep == Separator_CloseParen {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(types) == 0 {
|
2024-03-13 17:17:09 +01:00
|
|
|
return nil, p.error("empty tuple")
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
return &Type{Type: Type_Tuple, Value: TupleType{Types: types}}, nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if tok.Type == Type_Identifier {
|
2024-03-12 22:47:45 +01:00
|
|
|
return &Type{Type: Type_Named, Value: tok.Value}, nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-13 17:17:09 +01:00
|
|
|
return nil, p.error("expected type")
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: parse statement
|
|
|
|
p.nextToken()
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Block{Statements: statements}, nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
func (p *Parser) expectFunction() (*ParsedFunction, error) {
|
2024-03-11 22:05:36 +01:00
|
|
|
var err error
|
|
|
|
|
2024-03-12 22:00:03 +01:00
|
|
|
var name string
|
|
|
|
var parameters []ParsedParameter
|
2024-03-11 22:05:36 +01:00
|
|
|
var returnType *Type
|
2024-03-13 17:17:09 +01:00
|
|
|
var body *Block
|
2024-03-11 22:05:36 +01:00
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
returnType, err = p.expectType()
|
2024-03-11 22:05:36 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
name, err = p.expectIdentifier()
|
2024-03-12 22:00:03 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
_, err = p.expectSeparator(Separator_OpenParen)
|
2024-03-12 22:00:03 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
2024-03-12 22:47:45 +01:00
|
|
|
token := p.peekToken()
|
|
|
|
if token == nil {
|
2024-03-13 17:17:09 +01:00
|
|
|
return nil, p.error("incomplete function declaration")
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if token.Type == Type_Separator && token.Value.(Separator) == Separator_CloseParen {
|
2024-03-12 22:47:45 +01:00
|
|
|
p.nextToken()
|
2024-03-12 22:00:03 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(parameters) > 0 {
|
2024-03-12 22:47:45 +01:00
|
|
|
_, err := p.expectSeparator(Separator_Comma)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var paramType *Type
|
2024-03-12 22:47:45 +01:00
|
|
|
paramType, err = p.expectType()
|
2024-03-12 22:00:03 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var paramName string
|
2024-03-12 22:47:45 +01:00
|
|
|
paramName, err = p.expectIdentifier()
|
2024-03-12 22:00:03 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
parameters = append(parameters, ParsedParameter{Name: paramName, Type: *paramType})
|
|
|
|
}
|
|
|
|
|
2024-03-13 17:17:09 +01:00
|
|
|
body, err = p.expectBlock()
|
2024-03-12 22:00:03 +01:00
|
|
|
if err != nil {
|
2024-03-12 22:47:45 +01:00
|
|
|
return nil, err
|
2024-03-12 22:00:03 +01:00
|
|
|
}
|
|
|
|
|
2024-03-13 17:17:09 +01:00
|
|
|
return &ParsedFunction{Name: name, Parameters: parameters, ReturnType: *returnType, Body: *body}, nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
func (p *Parser) parseFile() (*ParsedFile, error) {
|
2024-03-11 22:05:36 +01:00
|
|
|
var err error
|
|
|
|
|
|
|
|
var functions []ParsedFunction
|
|
|
|
var imports []Import
|
|
|
|
|
2024-03-12 22:47:45 +01:00
|
|
|
for {
|
|
|
|
token := p.peekToken()
|
|
|
|
if token == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if token.Type == Type_Keyword && token.Value.(Keyword) == Keyword_Import {
|
2024-03-11 22:05:36 +01:00
|
|
|
var parsedImport *Import
|
2024-03-12 22:47:45 +01:00
|
|
|
parsedImport, err = p.expectImport()
|
2024-03-11 22:05:36 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
imports = append(imports, *parsedImport)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var parsedFunction *ParsedFunction
|
2024-03-12 22:47:45 +01:00
|
|
|
parsedFunction, err = p.expectFunction()
|
2024-03-11 22:05:36 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
functions = append(functions, *parsedFunction)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ParsedFile{Imports: imports, Functions: functions}, nil
|
|
|
|
}
|