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).
95 lines
1.9 KiB
Go
95 lines
1.9 KiB
Go
package lexer
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"aego/script/diag"
|
|
"aego/script/token"
|
|
)
|
|
|
|
func (l *Lexer) number(start int) token.Token {
|
|
kind := token.Int
|
|
hex := false
|
|
bad := ""
|
|
if l.at(0) == '0' && (l.at(1) == 'x' || l.at(1) == 'X') {
|
|
hex = true
|
|
l.off += 2
|
|
if l.run(isHexOrUnderscore) == 0 {
|
|
bad = "hexadecimal literal has no digits"
|
|
}
|
|
} else {
|
|
l.run(isDigitOrUnderscore)
|
|
if l.at(0) == '.' && isDigit(l.at(1)) {
|
|
kind = token.Float
|
|
l.off++
|
|
l.run(isDigitOrUnderscore)
|
|
} else if l.at(0) == '.' && !isLetter(l.at(1)) {
|
|
kind = token.Float
|
|
l.off++
|
|
bad = "float literal needs digits after the point, write 1.0"
|
|
}
|
|
if c := l.at(0); c == 'e' || c == 'E' {
|
|
kind = token.Float
|
|
l.off++
|
|
if c := l.at(0); c == '+' || c == '-' {
|
|
l.off++
|
|
}
|
|
if l.run(isDigitOrUnderscore) == 0 {
|
|
bad = "exponent has no digits"
|
|
}
|
|
}
|
|
}
|
|
if isIdentPart(l.at(0)) {
|
|
sfx := l.off
|
|
l.run(isIdentPart)
|
|
if bad == "" {
|
|
bad = fmt.Sprintf("invalid suffix %q on number", l.src[sfx:l.off])
|
|
}
|
|
}
|
|
text := string(l.src[start:l.off])
|
|
if bad == "" && !validUnderscores(text, hex) {
|
|
bad = "'_' must separate successive digits"
|
|
}
|
|
if bad != "" {
|
|
l.errorf(diag.MalformedNumber, start, l.off, "%s", bad)
|
|
}
|
|
return l.emit(kind, start, text, "")
|
|
}
|
|
|
|
func ParseInt(text string) (uint64, bool) {
|
|
s := strings.ReplaceAll(text, "_", "")
|
|
base := 10
|
|
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
|
|
s, base = s[2:], 16
|
|
}
|
|
v, err := strconv.ParseUint(s, base, 64)
|
|
return v, err == nil
|
|
}
|
|
|
|
func ParseFloat(text string) (float64, bool) {
|
|
v, err := strconv.ParseFloat(strings.ReplaceAll(text, "_", ""), 64)
|
|
return v, err == nil
|
|
}
|
|
|
|
func validUnderscores(s string, hex bool) bool {
|
|
start := 0
|
|
if hex {
|
|
start = 2
|
|
}
|
|
digit := isDigit
|
|
if hex {
|
|
digit = isHex
|
|
}
|
|
for i := start; i < len(s); i++ {
|
|
if s[i] != '_' {
|
|
continue
|
|
}
|
|
if i == start || i == len(s)-1 || !digit(s[i-1]) || !digit(s[i+1]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|