diff --git a/example/helloworld.lang b/example/helloworld.lang index 97a886f..172cd50 100644 --- a/example/helloworld.lang +++ b/example/helloworld.lang @@ -1,6 +1,7 @@ import lang.stdlib; (u8, u8) a(u8 a, u8 b) { + bool a = true; return a, b; } diff --git a/lexer.go b/lexer.go index 1e9e0fc..22470e7 100644 --- a/lexer.go +++ b/lexer.go @@ -9,8 +9,6 @@ import ( ) var Whitespace []rune = []rune{' ', '\t', '\r', '\n'} -var Separators []rune = []rune{'(', ')', '{', '}', ';', ','} -var Operators []rune = []rune{'=', '>', '<', '!', '+', '-', '*', '/', '%'} type LexType uint32 @@ -24,14 +22,20 @@ const ( type Keyword uint32 +var Keywords []string = []string{"import", "void", "return", "true", "false"} + const ( Keyword_Import Keyword = iota Keyword_Void Keyword_Return + Keyword_True + KeyWord_False ) type Separator uint32 +var Separators []rune = []rune{'(', ')', '{', '}', ';', ','} + const ( Separator_OpenParen Separator = iota Separator_CloseParen @@ -43,6 +47,8 @@ const ( type Operator uint32 +var Operators []rune = []rune{'=', '>', '<', '!', '+', '-', '*', '/', '%'} + const ( Operator_Equals Operator = iota Operator_Greater @@ -60,6 +66,7 @@ type LiteralType uint32 const ( Literal_String LiteralType = iota Literal_Number + Literal_Boolean ) type LexToken struct { @@ -168,7 +175,7 @@ func (l *Lexer) nextToken() (string, error) { token := "" for { r := l.peekRune() - if r == nil || slices.Contains(Whitespace, *r) || slices.Contains(Separators, *r) { + if r == nil || slices.Contains(Whitespace, *r) || slices.Contains(Separators, *r) || slices.Contains(Operators, *r) { break } @@ -183,6 +190,7 @@ func (l *Lexer) nextToken() (string, error) { } func parseNumber(raw string, numberType PrimitiveType) (any, error) { + // TODO: return compiler errors if isSignedInt(numberType) { return strconv.ParseInt(raw, 10, getBits(numberType)) } @@ -241,28 +249,21 @@ func (l *Lexer) parseToken(token string) (*LexToken, error) { return &LexToken{Type: Type_Literal, Position: l.Position, Value: Literal{Type: Literal_Number, Primitive: numberType, Value: number}}, nil } - switch token { - case "void": - return &LexToken{Type: Type_Keyword, Position: l.Position, Value: Keyword_Void}, nil - case "import": - return &LexToken{Type: Type_Keyword, Position: l.Position, Value: Keyword_Import}, nil - case "return": - return &LexToken{Type: Type_Keyword, Position: l.Position, Value: Keyword_Return}, nil - case "(": - return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator_OpenParen}, nil - case ")": - return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator_CloseParen}, nil - case "{": - return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator_OpenCurly}, nil - case "}": - return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator_CloseCurly}, nil - case ";": - return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator_Semicolon}, nil - case ",": - return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator_Comma}, nil - default: - return &LexToken{Type: Type_Identifier, Position: l.Position, Value: token}, nil + if len(runes) == 1 { + if idx := slices.Index(Separators, runes[0]); idx != -1 { + return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator(idx)}, nil + } + + if idx := slices.Index(Operators, runes[0]); idx != -1 { + return &LexToken{Type: Type_Operator, Position: l.Position, Value: Operator(idx)}, nil + } } + + if idx := slices.Index(Keywords, token); idx != -1 { + return &LexToken{Type: Type_Keyword, Position: l.Position, Value: Keyword(idx)}, nil + } + + return &LexToken{Type: Type_Identifier, Position: l.Position, Value: token}, nil } func lexer(program string) ([]LexToken, error) { diff --git a/main.go b/main.go index c80fa7d..a7d48cf 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,10 @@ func main() { tokens, err := lexer(string(content)) if err != nil { + if c, ok := err.(CompilerError); ok { + log.Fatalln(err, "\nv- here\n"+string([]rune(string(content))[c.Position:])) + } + log.Fatalln(err) } diff --git a/parser.go b/parser.go index 1e5e217..c1cb15a 100644 --- a/parser.go +++ b/parser.go @@ -80,8 +80,7 @@ type AssignmentExpression struct { } type LiteralExpression struct { - Type PrimitiveType - Value any + Literal Literal } type VariableReferenceExpression struct { @@ -349,6 +348,8 @@ func (p *Parser) tryUnaryExpression() (*Expression, error) { } if token.Type == Type_Separator && token.Value.(Separator) == Separator_OpenParen { + // TODO: cast + paren, err := pCopy.tryParanthesizedExpression() if err != nil { *p = pCopy @@ -359,8 +360,21 @@ func (p *Parser) tryUnaryExpression() (*Expression, error) { *p = pCopy return paren, nil } + } - // TODO: cast + 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 { @@ -406,8 +420,9 @@ func (p *Parser) tryTupleExpression() (*Expression, error) { token := pCopy.peekToken() if token == nil || token.Type != Type_Separator || token.Value.(Separator) != Separator_Comma { - if len(members) <= 1 { - return nil, nil // None/One experession -> not a tuple expression + if len(members) == 1 { + *p = pCopy + return expr, nil } break @@ -440,11 +455,6 @@ func (p *Parser) expect(try func() (*Expression, error), message string) (*Expre func (p *Parser) tryDeclareLocalVariableStatement() (*Statement, error) { pCopy := p.copy() - token := pCopy.nextToken() - if token == nil { - return nil, nil - } - variableType, err := pCopy.tryType() if err != nil { *p = pCopy @@ -462,7 +472,7 @@ func (p *Parser) tryDeclareLocalVariableStatement() (*Statement, error) { variableName := name.Value.(string) - token = pCopy.nextToken() + token := pCopy.nextToken() if token.Type == Type_Separator && token.Value.(Separator) == Separator_Semicolon { return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, Initializer: nil}}, nil } @@ -471,13 +481,13 @@ func (p *Parser) tryDeclareLocalVariableStatement() (*Statement, error) { return nil, nil } - initializer, err := p.expect(p.tryExpression, "expected initializer expression") + initializer, err := pCopy.expect(pCopy.tryExpression, "expected initializer expression") if err != nil { *p = pCopy return nil, err } - _, err = p.expectSeparator(Separator_Semicolon) + _, err = pCopy.expectSeparator(Separator_Semicolon) if err != nil { *p = pCopy return nil, err @@ -528,6 +538,15 @@ func (p *Parser) expectStatement() (*Statement, error) { 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") } diff --git a/types.go b/types.go index 8ae8c84..9a24e0f 100644 --- a/types.go +++ b/types.go @@ -14,7 +14,9 @@ type Lang_U16 uint16 type Lang_U32 uint32 type Lang_U64 uint64 -var NumberTypeNames = [...]string{"i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "f32", "f64"} +type Lang_Bool bool + +var NumberTypeNames = [...]string{"i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "f32", "f64", "bool"} type PrimitiveType uint32 @@ -29,6 +31,7 @@ const ( Primitive_U64 Primitive_F32 Primitive_F64 + Primitive_Bool ) const InvalidValue = 0xEEEEEE // Magic value