Skip to content

Commit

Permalink
feat: first support to enums
Browse files Browse the repository at this point in the history
  • Loading branch information
pmqueiroz committed Nov 29, 2024
1 parent 7c9b094 commit 99eaaab
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 65 deletions.
43 changes: 42 additions & 1 deletion ast/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/pmqueiroz/umbra/exception"
"github.com/pmqueiroz/umbra/tokens"
"github.com/pmqueiroz/umbra/types"
)

type Parser struct {
Expand Down Expand Up @@ -99,10 +100,16 @@ func (p *Parser) function() Statement {
paramName := p.consume("Expect parameter name.", tokens.IDENTIFIER)
variadic := p.match(tokens.VARIADIC)
paramType := p.consume("Expect parameter type.", tokens.DATA_TYPES...)
parsedParamType, err := types.ParseTokenType(paramType.Type)

if err != nil {
p.throw("Invalid parameter type.")
}

nullable := p.match(tokens.HOOK)
params = append(params, Parameter{
Name: paramName,
Type: paramType,
Type: parsedParamType,
Variadic: variadic,
Nullable: nullable,
})
Expand Down Expand Up @@ -536,6 +543,37 @@ func (p *Parser) importStatement() Statement {
}
}

func (p *Parser) enumStatement() Statement {
name := p.consume("Expect enum name.", tokens.IDENTIFIER)

p.consume("Expect '{' before enum body.", tokens.LEFT_BRACE)

members := make(map[string]EnumMember)

for !p.check(tokens.RIGHT_BRACE) && !p.isAtEOF() {
memberName := p.consume("Expect enum member name.", tokens.IDENTIFIER)
var value Expression

if p.match(tokens.EQUAL) {
value = p.expression()
}

members[memberName.Lexeme] = EnumMember{
Name: memberName.Lexeme,
Value: value,
}

if p.match(tokens.RIGHT_BRACE) {
break
}
}

return EnumStatement{
Name: name,
Members: members,
}
}

func (p *Parser) printStatement(channel PrintChannel) Statement {
value := p.expression()
return PrintStatement{
Expand Down Expand Up @@ -652,6 +690,9 @@ func (p *Parser) statement() Statement {
if p.match(tokens.IMPORT) {
return p.importStatement()
}
if p.match(tokens.ENUM) {
return p.enumStatement()
}
if p.match(tokens.LEFT_BRACE) {
blockStatement, _ := p.block()
return blockStatement
Expand Down
25 changes: 24 additions & 1 deletion ast/statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ast

import (
"github.com/pmqueiroz/umbra/tokens"
"github.com/pmqueiroz/umbra/types"
)

type Statement interface {
Expand All @@ -17,7 +18,7 @@ type ExpressionStatement struct {

type Parameter struct {
Name tokens.Token
Type tokens.Token
Type types.UmbraType
Variadic bool
Nullable bool
}
Expand Down Expand Up @@ -66,6 +67,28 @@ type ImportStatement struct {
Path tokens.Token
}

type EnumMember struct {
Name string
Value Expression
Signature string
}

type EnumStatement struct {
Name tokens.Token
Members map[string]EnumMember
Signature string
}

func (e *EnumStatement) Get(name tokens.Token) (EnumMember, bool) {
member, ok := e.Members[name.Lexeme]

return member, ok
}

func (e *EnumStatement) ValidMember(member EnumMember) bool {
return e.Signature == member.Signature
}

type VarStatement struct {
Name tokens.Token
Initializer Expression
Expand Down
6 changes: 3 additions & 3 deletions environment/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
"os"

"github.com/pmqueiroz/umbra/exception"
"github.com/pmqueiroz/umbra/tokens"
"github.com/pmqueiroz/umbra/types"
)

type Variable struct {
Data interface{}
DataType tokens.TokenType
DataType types.UmbraType
Nullable bool
private bool
native bool
Expand Down Expand Up @@ -60,7 +60,7 @@ func (env *Environment) Set(name string, value interface{}) bool {
return false
}

func (env *Environment) Create(name string, value interface{}, dataType tokens.TokenType, nullable bool, internal bool) bool {
func (env *Environment) Create(name string, value interface{}, dataType types.UmbraType, nullable bool, internal bool) bool {
if _, exists := env.Get(name, true); exists {
fmt.Println(exception.NewRuntimeError("RT001", name))
os.Exit(1)
Expand Down
15 changes: 15 additions & 0 deletions examples/enums.u
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import "io"

enum Direction {
North
South
East
West
}

const teste Direction = Direction.North
io::printLn("North:", Direction.North)

## Next Steps
# - [] Add support for generic enums
# - [] Add enum pattern matching
2 changes: 2 additions & 0 deletions exception/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ var Messages = map[string]string{
"RT031": "invalid internal function call",
"RT032": "cannot %s file: %s",
"RT033": "all paths must be strings",
"RT034": "enum member '%s' does not exits",
"RT035": "cannot use '%s' as a type",
"GN001": "cannot find module '%s'",
}
48 changes: 32 additions & 16 deletions interpreter/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func toFloat64(value interface{}) (float64, error) {
case rune:
return float64(v), nil
default:
return 0, exception.NewRuntimeError("RT026", types.ParseUmbraType(value))
return 0, exception.NewRuntimeError("RT026", types.SafeParseUmbraType(value))
}
}

Expand Down Expand Up @@ -76,7 +76,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
return nil, exception.NewRuntimeError("RT002", target.Name.Lexeme)
}

typeErr := types.CheckType(variable.DataType, value, variable.Nullable)
typeErr := types.CheckPrimitiveType(variable.DataType, value, variable.Nullable)

if typeErr != nil {
return nil, typeErr
Expand Down Expand Up @@ -119,10 +119,10 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
obj[int(idx)] = value
return value, nil
default:
return nil, exception.NewRuntimeError("RT005", types.ParseUmbraType(obj))
return nil, exception.NewRuntimeError("RT005", types.SafeParseUmbraType(obj))
}
default:
return nil, exception.NewRuntimeError("RT006", types.ParseUmbraType(target))
return nil, exception.NewRuntimeError("RT006", types.SafeParseUmbraType(target))
}
case ast.BinaryExpression:
left, err := Evaluate(expr.Left, env)
Expand Down Expand Up @@ -157,7 +157,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
return leftVal + rightFloat, nil
}
}
return nil, exception.NewRuntimeError("RT007", types.ParseUmbraType(left), types.ParseUmbraType(right))
return nil, exception.NewRuntimeError("RT007", types.SafeParseUmbraType(left), types.SafeParseUmbraType(right))
case tokens.MINUS:
switch leftVal := left.(type) {
case float64:
Expand All @@ -173,7 +173,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
return leftVal - rightRune, nil
}
}
return nil, exception.NewRuntimeError("RT027", types.ParseUmbraType(left), types.ParseUmbraType(right))
return nil, exception.NewRuntimeError("RT027", types.SafeParseUmbraType(left), types.SafeParseUmbraType(right))
case tokens.STAR:
return left.(float64) * right.(float64), nil
case tokens.SLASH:
Expand All @@ -188,7 +188,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
return math.Mod(leftFloat, rightFloat), nil
}

return nil, exception.NewRuntimeError("RT009", types.ParseUmbraType(left), types.ParseUmbraType(right))
return nil, exception.NewRuntimeError("RT009", types.SafeParseUmbraType(left), types.SafeParseUmbraType(right))
case tokens.GREATER_THAN:
leftVal, err := toFloat64(left)
if err != nil {
Expand Down Expand Up @@ -251,7 +251,13 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
case tokens.NOT:
return !right.(bool), nil
case tokens.TYPE_OF:
return types.ParseUmbraType(right), nil
parsedType, err := types.ParseUmbraType(right)

if err != nil {
return nil, err
}

return parsedType, nil
case tokens.TILDE:
switch parsedRight := right.(type) {
case []interface{}:
Expand All @@ -261,7 +267,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
case string:
return float64(len(parsedRight)), nil
default:
return nil, exception.NewRuntimeError("RT011", types.ParseUmbraType(parsedRight))
return nil, exception.NewRuntimeError("RT011", types.SafeParseUmbraType(parsedRight))
}
case tokens.RANGE:
switch parsedRight := right.(type) {
Expand All @@ -274,7 +280,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
}
return result, nil
default:
return nil, exception.NewRuntimeError("RT012", types.ParseUmbraType(parsedRight))
return nil, exception.NewRuntimeError("RT012", types.SafeParseUmbraType(parsedRight))
}
default:
return nil, exception.NewRuntimeError("RT013", expr.Operator.Lexeme)
Expand All @@ -298,27 +304,27 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
return nil, err
}

typeErr := types.CheckType(param.Type.Type, argValue, param.Nullable)
typeErr := types.CheckPrimitiveType(param.Type, argValue, param.Nullable)
if typeErr != nil {
return nil, typeErr
}

variadicArgs = append(variadicArgs, argValue)
}
funcEnv.Create(param.Name.Lexeme, variadicArgs, param.Type.Type, param.Nullable, false)
funcEnv.Create(param.Name.Lexeme, variadicArgs, param.Type, param.Nullable, false)
break
} else {
argValue, err := Evaluate(expr.Arguments[i], env)
if err != nil {
return nil, err
}

typeErr := types.CheckType(param.Type.Type, argValue, param.Nullable)
typeErr := types.CheckPrimitiveType(param.Type, argValue, param.Nullable)
if typeErr != nil {
return nil, typeErr
}

funcEnv.Create(param.Name.Lexeme, argValue, param.Type.Type, param.Nullable, false)
funcEnv.Create(param.Name.Lexeme, argValue, param.Type, param.Nullable, false)
}
}

Expand Down Expand Up @@ -449,8 +455,18 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
return nil, exception.NewRuntimeError("RT004", idx)
}
return getElementAt(obj, int(idx)), nil
case ast.EnumStatement:
if prop, ok := expr.Property.(ast.VariableExpression); ok {
member, ok := obj.Get(prop.Name)
if !ok {
return nil, exception.NewRuntimeError("RT034", prop.Name.Lexeme)
}

return member, nil
}
return nil, exception.NewRuntimeError("RT020")
default:
return nil, exception.NewRuntimeError("RT016", types.ParseUmbraType(obj))
return nil, exception.NewRuntimeError("RT016", types.SafeParseUmbraType(obj))
}
case ast.NamespaceMemberExpression:
if variableExpr, ok := expr.Namespace.(ast.VariableExpression); ok {
Expand All @@ -471,7 +487,7 @@ func Evaluate(expression ast.Expression, env *environment.Environment) (interfac
return nil, err
}

defaultError := exception.NewRuntimeError("RT028", types.ParseUmbraType(value), types.ParseTokenType(expr.Type.Type))
defaultError := exception.NewRuntimeError("RT028", types.SafeParseUmbraType(value), types.SafeParseUmbraType(expr.Type.Type))

switch expr.Type.Type {
case tokens.STR_TYPE:
Expand Down
Loading

0 comments on commit 99eaaab

Please sign in to comment.