Files
sanspie 17cb5e4291 Stage 2-5: assets, scene/ECS, editor, 3D, and Aego Script
Stage 2: render pipeline and UI toolkit over gpu/platform.
Stage 3: schema registry, asset pipeline + runtime loader, scene graph and ECS.
Stage 4: editor — docking, inspector, scene/game views, play mode, asset browser, console, profiler; 4.5 adds the 3D foundation (mixed 2D/3D spaces, meshes, depth).
Stage 5: Aego Script — compiler (lexer/parser/types/resolver/checker/asm/compiler), bytecode with verifier and register VM, instance runtime, stdlib, native bindings, and editor/CLI integration (aego run/edit/check/test/dump).
2026-10-08 13:17:28 +03:00

165 lines
3.6 KiB
Go

package checker
import (
"math"
"aego/script/token"
"aego/script/types"
)
const (
whyOverflow = "overflows"
whyDivZero = "division by zero"
whyInvalid = "is not a valid constant operation"
)
func foldUnary(op token.Kind, x *types.Const) (*types.Const, string) {
switch {
case op == token.Sub && x.Kind == types.CInt:
if x.Int == math.MinInt64 {
return nil, whyOverflow
}
return types.IntConst(-x.Int), ""
case op == token.Sub && x.Kind == types.CFloat:
return types.FloatConst(-x.Float), ""
case op == token.Not && x.Kind == types.CBool:
return types.BoolConst(!x.Bool), ""
}
return nil, whyInvalid
}
func compare(op token.Kind, c int) *types.Const {
switch op {
case token.Eq:
return types.BoolConst(c == 0)
case token.Ne:
return types.BoolConst(c != 0)
case token.Lt:
return types.BoolConst(c < 0)
case token.Le:
return types.BoolConst(c <= 0)
case token.Gt:
return types.BoolConst(c > 0)
case token.Ge:
return types.BoolConst(c >= 0)
}
return nil
}
func cmp[T int64 | float64 | string](a, b T) int {
switch {
case a < b:
return -1
case a > b:
return 1
}
return 0
}
func numeric(c *types.Const) bool {
return c.Kind == types.CInt || c.Kind == types.CFloat
}
func foldBinary(op token.Kind, a, b *types.Const, single bool) (*types.Const, string) {
switch {
case a.Kind == types.CBool && b.Kind == types.CBool:
switch op {
case token.AndAnd:
return types.BoolConst(a.Bool && b.Bool), ""
case token.OrOr:
return types.BoolConst(a.Bool || b.Bool), ""
case token.Eq:
return types.BoolConst(a.Bool == b.Bool), ""
case token.Ne:
return types.BoolConst(a.Bool != b.Bool), ""
}
case a.Kind == types.CString && b.Kind == types.CString:
if op == token.Add {
return types.StringConst(a.Str + b.Str), ""
}
if r := compare(op, cmp(a.Str, b.Str)); r != nil {
return r, ""
}
case a.Kind == types.CInt && b.Kind == types.CInt:
return foldInt(op, a.Int, b.Int)
case numeric(a) && numeric(b):
return foldFloat(op, a.Float64(), b.Float64(), single)
}
return nil, whyInvalid
}
func foldInt(op token.Kind, x, y int64) (*types.Const, string) {
if r := compare(op, cmp(x, y)); r != nil {
return r, ""
}
switch op {
case token.Add:
r := x + y
if (x > 0 && y > 0 && r < 0) || (x < 0 && y < 0 && r >= 0) {
return nil, whyOverflow
}
return types.IntConst(r), ""
case token.Sub:
r := x - y
if (x >= 0 && y < 0 && r < 0) || (x < 0 && y > 0 && r >= 0) {
return nil, whyOverflow
}
return types.IntConst(r), ""
case token.Mul:
if x == 0 || y == 0 {
return types.IntConst(0), ""
}
r := x * y
if r/y != x || (x == -1 && y == math.MinInt64) || (y == -1 && x == math.MinInt64) {
return nil, whyOverflow
}
return types.IntConst(r), ""
case token.Quo:
if y == 0 {
return nil, whyDivZero
}
if x == math.MinInt64 && y == -1 {
return nil, whyOverflow
}
return types.IntConst(x / y), ""
case token.Rem:
if y == 0 {
return nil, whyDivZero
}
if y == -1 {
return types.IntConst(0), ""
}
return types.IntConst(x % y), ""
}
return nil, whyInvalid
}
func foldFloat(op token.Kind, x, y float64, single bool) (*types.Const, string) {
if r := compare(op, cmp(x, y)); r != nil {
return r, ""
}
var r float64
switch op {
case token.Add:
r = x + y
case token.Sub:
r = x - y
case token.Mul:
r = x * y
case token.Quo:
if y == 0 {
return nil, whyDivZero
}
r = x / y
default:
return nil, whyInvalid
}
if math.IsInf(r, 0) || (single && math.Abs(r) > math.MaxFloat32) {
return nil, whyOverflow
}
if single {
r = float64(float32(r))
}
return types.FloatConst(r), ""
}