Skip to content

Commit

Permalink
CORE: Rewrite handling of casts
Browse files Browse the repository at this point in the history
CORE: Add int promotion (implicit casts)
  • Loading branch information
MineGame159 committed Jan 24, 2024
1 parent 9fd9f20 commit e774f43
Show file tree
Hide file tree
Showing 16 changed files with 502 additions and 388 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
example/build
tests/build

fireball
/fireball
105 changes: 105 additions & 0 deletions core/ast/casts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package ast

type CastKind uint8

const (
None CastKind = iota

Truncate
Extend

Int2Float
Float2Int
)

func GetCast(from, to Type) (CastKind, bool) {
if from.Equals(to) {
return None, true
}

switch from := from.Resolved().(type) {
// Primitive -> ...
case *Primitive:
switch to := to.Resolved().(type) {
// Primitive -> Primitive
case *Primitive:
if (IsInteger(from.Kind) && IsInteger(to.Kind)) || (IsFloating(from.Kind) && IsFloating(to.Kind)) {
if to.Size() > from.Size() {
return Extend, true
} else if to.Size() < from.Size() {
return Truncate, true
} else {
return None, true
}
} else if IsInteger(from.Kind) && IsFloating(to.Kind) {
return Int2Float, true
} else if IsFloating(from.Kind) && IsInteger(to.Kind) {
return Float2Int, true
}

// Primitive (Integer) -> Enum
case *Enum:
if IsInteger(from.Kind) {
if to.Size() > from.Size() {
return Extend, true
} else if to.Size() < from.Size() {
return Truncate, true
} else {
return None, true
}
}
}

// Pointer -> Pointer, Func
case *Pointer:
switch to.Resolved().(type) {
case *Pointer, *Func:
return None, true
}

// Enum -> Primitive (Integer)
case *Enum:
if to, ok := As[*Primitive](to); ok && IsInteger(to.Kind) {
if to.Size() > from.Size() {
return Extend, true
} else if to.Size() < from.Size() {
return Truncate, true
} else {
return None, true
}
}
}

return None, false
}

func GetImplicitCast(from, to Type) (CastKind, bool) {
if from.Equals(to) {
return None, true
}

switch from := from.Resolved().(type) {
// Primitive -> Primitive
case *Primitive:
if to, ok := As[*Primitive](to); ok {
// Primitive (smaller integer / floating) -> Primitive (bigger integer / floating)
if ((IsInteger(from.Kind) && IsInteger(to.Kind)) || (IsFloating(from.Kind) && IsFloating(to.Kind))) && to.Size() > from.Size() {
return Extend, true
}

// Primitive (same integer) -> Primitive (same floating)
// TODO: Allow converting a smaller integer to the next bigger floating (eg. i16 -> f32)
if IsInteger(from.Kind) && IsFloating(to.Kind) && to.Size() == from.Size() {
return Int2Float, true
}
}

// Pointer -> Pointer (*void)
case *Pointer:
if to, ok := As[*Pointer](to); ok && IsPrimitive(to.Pointee, Void) {
return None, true
}
}

return None, false
}
7 changes: 5 additions & 2 deletions core/ast/cst2ast/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cst2ast
import (
"fireball/core/ast"
"fireball/core/cst"
"fireball/core/scanner"
"strconv"
)

Expand All @@ -23,10 +24,12 @@ func (c *converter) convertType(node cst.Node) ast.Type {
}

func (c *converter) convertIdentifierType(node cst.Node) ast.Type {
if len(node.Children) == 1 {
identifier := node.Get(scanner.Identifier)

if identifier != nil {
kind := ast.Unknown

switch node.Children[0].Token.Lexeme {
switch identifier.Token.Lexeme {
case "void":
kind = ast.Void
case "bool":
Expand Down
1 change: 0 additions & 1 deletion core/ast/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ type Type interface {
Align() uint32

Equals(other Type) bool
CanAssignTo(other Type) bool

Resolved() Type

Expand Down
74 changes: 7 additions & 67 deletions core/ast/types_manual.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ func (p *Primitive) Equals(other Type) bool {
return IsPrimitive(other, p.Kind)
}

func (p *Primitive) CanAssignTo(other Type) bool {
return IsPrimitive(other, p.Kind)
}

// Pointer

func (p *Pointer) Size() uint32 {
Expand All @@ -76,14 +72,6 @@ func (p *Pointer) Equals(other Type) bool {
return false
}

func (p *Pointer) CanAssignTo(other Type) bool {
if p2, ok := As[*Pointer](other); ok {
return IsPrimitive(p2.Pointee, Void) || typesEquals(p.Pointee, p2.Pointee)
}

return false
}

// Array

func (a *Array) Size() uint32 {
Expand All @@ -102,10 +90,6 @@ func (a *Array) Equals(other Type) bool {
return false
}

func (a *Array) CanAssignTo(other Type) bool {
return a.Equals(other)
}

// Resolvable

func (r *Resolvable) Size() uint32 {
Expand All @@ -132,14 +116,6 @@ func (r *Resolvable) Equals(other Type) bool {
return r.Resolved().Equals(other.Resolved())
}

func (r *Resolvable) CanAssignTo(other Type) bool {
if r.Resolved() == nil {
panic("ast.Resolvable.Equals() - Not resolved")
}

return r.Resolved().CanAssignTo(other.Resolved())
}

// Struct

func (s *Struct) Size() uint32 {
Expand All @@ -163,23 +139,7 @@ func (s *Struct) Align() uint32 {
}

func (s *Struct) Equals(other Type) bool {
if s2, ok := As[*Struct](other); ok {
return tokensEquals(s.Name, s2.Name) && slices.EqualFunc(s.Fields, s2.Fields, fieldEquals)
}

return false
}

func fieldEquals(v1, v2 *Field) bool {
return tokensEquals(v1.Name, v2.Name) && typesEquals(v1.Type, v2.Type)
}

func (s *Struct) CanAssignTo(other Type) bool {
if s2, ok := As[*Struct](other); ok {
return slices.EqualFunc(s.Fields, s2.Fields, fieldEquals)
}

return false
return s == other.Resolved()
}

func (s *Struct) Resolved() Type {
Expand All @@ -201,19 +161,7 @@ func (e *Enum) Align() uint32 {
}

func (e *Enum) Equals(other Type) bool {
if e2, ok := As[*Enum](other); ok {
return typesEquals(e.ActualType, e2.ActualType) && slices.EqualFunc(e.Cases, e2.Cases, enumCaseEquals)
}

return false
}

func enumCaseEquals(v1, v2 *EnumCase) bool {
return tokensEquals(v1.Name, v2.Name) && v1.ActualValue == v2.ActualValue
}

func (e *Enum) CanAssignTo(other Type) bool {
return e.Equals(other)
return e == other.Resolved()
}

func (e *Enum) Resolved() Type {
Expand All @@ -236,7 +184,11 @@ func (f *Func) Align() uint32 {

func (f *Func) Equals(other Type) bool {
if f2, ok := As[*Func](other); ok {
return tokensEquals(f.Name, f2.Name) && typesEquals(f.Returns, f2.Returns) && slices.EqualFunc(f.Params, f2.Params, paramEquals)
if f.Name != nil && f2.Name != nil {
return f == f2
}

return typesEquals(f.Returns, f2.Returns) && slices.EqualFunc(f.Params, f2.Params, paramEquals)
}

return false
Expand All @@ -246,14 +198,6 @@ func paramEquals(v1, v2 *Param) bool {
return typesEquals(v1.Type, v2.Type)
}

func (f *Func) CanAssignTo(other Type) bool {
if f2, ok := As[*Func](other); ok {
return typesEquals(f.Returns, f2.Returns) && slices.EqualFunc(f.Params, f2.Params, paramEquals)
}

return false
}

func (f *Func) Resolved() Type {
return f
}
Expand Down Expand Up @@ -332,10 +276,6 @@ func (t *typePrinter) VisitNode(node Node) {

// Utils

func tokensEquals(t1, t2 *Token) bool {
return (t1 == nil && t2 == nil) || (t1 != nil && t2 != nil && t1.String() == t2.String())
}

func typesEquals(t1, t2 Type) bool {
return (t1 == nil && t2 == nil) || (t1 != nil && t2 != nil && t1.Equals(t2))
}
10 changes: 10 additions & 0 deletions core/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ func (c *checker) expectPrimitiveValue(expr ast.Expr, kind ast.PrimitiveKind) {
}
}

func (c *checker) checkRequired(required ast.Type, expr ast.Expr) {
if required == nil || expr == nil || expr.Result().Kind == ast.InvalidResultKind {
return
}

if _, ok := ast.GetImplicitCast(expr.Result().Type, required); !ok {
c.error(expr, "Expected a '%s' but got a '%s'", ast.PrintType(required), ast.PrintType(expr.Result().Type))
}
}

// ast.Visit

func (c *checker) VisitNode(node ast.Node) {
Expand Down
Loading

0 comments on commit e774f43

Please sign in to comment.