Language: yield/signal/coroutine become keywords, start is contextual. Coroutine methods (func Name() coroutine) with start and yield / wait / frames / fixed / await; signals with Emit/Connect and Connection handles; ScriptRef<T> and node.GetScript<T>() for fields, plain methods and signals of other scripts. Diagnostics AES2050-AES2059. Bytecode v3: ScriptRefs, XFields, XMethods and Connects tables resolved by name in vm.Link, per-script signals, coroutine flag on functions; new opcodes START/YIELD/EMIT/CONNECT/DISCONNECT/CONNECTED/COSTOP/CORUN/ SELFREF/GETSCRIPT/GETXF/SETXF/CALLR, checked by verify. Runtime: script/coroutine scheduler (frames/fixed counters, time heap, await lists; only due coroutines are visited) and script/signal registry (snapshot per Emit, deferred removal, no allocations in steady state). Hot reload cancels coroutines of changed scripts, rebinds connections by name and signature, then runs Reloaded. Tooling: script/build removed, aego check/test/dump run on the workspace (test builds include _test.aes); langsvc completion, hover, signatures and semantic tokens for the new syntax; editor highlighting, snippets with placeholders, coroutine/signal profiler graphs and stats. Docs (script.md sections 16-19, editor.md, ARCHITECTURE), test fixtures and golden files, platformer enemies wired to the player, scriptreload demo, and run.Config.FrameTime for deterministic headless fixed steps. Modules (6.5): engine/module registry and ComponentBuilder are the one registration path for engine subsystems and native code; render2d, render3d and script/engine are modules over scene/frame, the script component moved to scene/scriptcomp, and scene/components and scene/systems are gone. Editor inspector, templates, picking shapes and extension hooks are driven by descriptors and editor/builtins; sdk gains Component/EditorHook; depcheck checks the module graph and the aego_runtime graph. Also: stop tracking built tool binaries; gl33 activates the scratch unit before binding for texture updates. Verification: typed component method adapters (Method0..2, Proc0..2) with direct field access and no allocations; app.Build as the single construction path for run and the editor; modules attach per scene document; depcheck aego_editor pass; diagnostic codes in one place and every code covered by a test; indexed coroutine heaps with eager cancel; dead component references and nil intrinsics fault with AES6004, host faults keep their code; GC roots of struct fields checked in verify and vm.Link; 255-field limit; AES4010 for script packages shadowing host modules; architecture registry D111-D147. Golden: the harness renders on the thread that owns the GL context, ReadPixels returns rows in memory order (render targets are top-down per D17), and the first rendering references are committed.
148 lines
3.1 KiB
Go
148 lines
3.1 KiB
Go
package langsvc
|
|
|
|
import (
|
|
"aego/script/ast"
|
|
"aego/script/checker"
|
|
"aego/script/resolver"
|
|
"aego/script/source"
|
|
"aego/script/types"
|
|
"aego/script/workspace"
|
|
)
|
|
|
|
func contains(sp source.Span, off uint32) bool {
|
|
return sp.Start <= off && off <= sp.End
|
|
}
|
|
|
|
func pathTo(n ast.Node, off uint32, stack []ast.Node, fn func(ast.Node, []ast.Node)) {
|
|
if n == nil || !contains(n.Span(), off) {
|
|
return
|
|
}
|
|
fn(n, stack)
|
|
stack = append(stack, n)
|
|
for _, c := range ast.Children(n) {
|
|
pathTo(c, off, stack, fn)
|
|
}
|
|
}
|
|
|
|
func identAt(f *ast.File, off uint32) (*ast.Ident, []ast.Node) {
|
|
var best *ast.Ident
|
|
var bestStack []ast.Node
|
|
score := 0
|
|
pathTo(f, off, nil, func(n ast.Node, stack []ast.Node) {
|
|
id, ok := n.(*ast.Ident)
|
|
if !ok || id.Name == "_" || id.Sp.End == id.Sp.Start {
|
|
return
|
|
}
|
|
s := 1
|
|
if off < id.Sp.End {
|
|
s = 2
|
|
}
|
|
if s > score {
|
|
best, score = id, s
|
|
bestStack = append([]ast.Node(nil), stack...)
|
|
}
|
|
})
|
|
return best, bestStack
|
|
}
|
|
|
|
func object(a *checker.Analysis, id *ast.Ident) *resolver.Object {
|
|
if o := a.Uses[id]; o != nil {
|
|
return o
|
|
}
|
|
return a.Defs[id]
|
|
}
|
|
|
|
func selectorWith(stack []ast.Node, id *ast.Ident) *ast.SelectorExpr {
|
|
if n := len(stack); n > 0 {
|
|
if sel, ok := stack[n-1].(*ast.SelectorExpr); ok && sel.Sel == id {
|
|
return sel
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func callOf(stack []ast.Node, sel *ast.SelectorExpr) *ast.CallExpr {
|
|
if n := len(stack); n > 1 {
|
|
if call, ok := stack[n-2].(*ast.CallExpr); ok && call.Fun == ast.Expr(sel) {
|
|
return call
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func componentArg(a *checker.Analysis, id *ast.Ident, stack []ast.Node) *checker.Call {
|
|
n := len(stack)
|
|
if n < 2 {
|
|
return nil
|
|
}
|
|
nt, ok := stack[n-1].(*ast.NamedType)
|
|
if !ok || nt.Name != id {
|
|
return nil
|
|
}
|
|
call, ok := stack[n-2].(*ast.CallExpr)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
k := a.Calls[call]
|
|
if k == nil || (k.Kind != checker.CallGetComponent && k.Kind != checker.CallHasComponent) {
|
|
return nil
|
|
}
|
|
return k
|
|
}
|
|
|
|
func loc(snap *workspace.Snapshot, sp source.Span) Location {
|
|
return Location{File: sp.File, Path: snap.Sources.Path(sp.File), Span: sp}
|
|
}
|
|
|
|
func structDecls(a *checker.Analysis, fn func(t *types.Type, d *ast.StructDecl) bool) {
|
|
for _, p := range a.Order {
|
|
cp := a.Packages[p]
|
|
if cp == nil || cp.Res == nil {
|
|
continue
|
|
}
|
|
for _, o := range cp.Res.Scope.Objects() {
|
|
d, ok := o.Decl.(*ast.StructDecl)
|
|
if ok && o.Type != nil && !fn(o.Type, d) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func fieldDecl(a *checker.Analysis, f *types.Field) *ast.Field {
|
|
var out *ast.Field
|
|
structDecls(a, func(t *types.Type, d *ast.StructDecl) bool {
|
|
for _, tf := range t.Fields {
|
|
if tf != f {
|
|
continue
|
|
}
|
|
for _, df := range d.Fields {
|
|
if df.Name.Name == f.Name {
|
|
out = df
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
return out
|
|
}
|
|
|
|
func structField(a *checker.Analysis, o *resolver.Object) *types.Field {
|
|
fd, ok := o.Decl.(*ast.Field)
|
|
if o.Kind != resolver.ObjField || !ok {
|
|
return nil
|
|
}
|
|
var out *types.Field
|
|
structDecls(a, func(t *types.Type, d *ast.StructDecl) bool {
|
|
for _, df := range d.Fields {
|
|
if df == fd {
|
|
out = t.Field(o.Name)
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
return out
|
|
}
|