Added SCRIPT LIBRARY block for reusable StarLark code.
This commit is contained in:
parent
617f67ecb5
commit
c6b67f8044
9 changed files with 448 additions and 39 deletions
20
examples/script_library_demo/cm.sh
Executable file
20
examples/script_library_demo/cm.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
# Define filename as variable
|
||||
PROGNAME="script_library_demo"
|
||||
# Only set C65LIBPATH if not already defined
|
||||
if [ -z "$C65LIBPATH" ]; then
|
||||
export C65LIBPATH=$(readlink -f "../../lib")
|
||||
fi
|
||||
# Compile
|
||||
c65gm -in ${PROGNAME}.c65 -out ${PROGNAME}.s
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Compilation terminated"
|
||||
exit 1
|
||||
fi
|
||||
echo assemble.
|
||||
acme ${PROGNAME}.s
|
||||
if [ -f ${PROGNAME}.prg ]; then
|
||||
rm ${PROGNAME}.prg
|
||||
fi
|
||||
# main.bin ${PROGNAME}.prg
|
||||
mv main.bin main.prg
|
||||
85
examples/script_library_demo/script_library_demo.c65
Normal file
85
examples/script_library_demo/script_library_demo.c65
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//-----------------------------------------------------------
|
||||
// SCRIPT LIBRARY Demo
|
||||
// Demonstrates reusable Starlark functions defined in
|
||||
// SCRIPT LIBRARY blocks and called from SCRIPT blocks
|
||||
//-----------------------------------------------------------
|
||||
|
||||
#INCLUDE <c64start.c65>
|
||||
|
||||
GOTO start
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// SCRIPT LIBRARY: Define reusable code generation functions
|
||||
//-----------------------------------------------------------
|
||||
SCRIPT LIBRARY
|
||||
def emit_nops(count):
|
||||
for i in range(count):
|
||||
print(" nop")
|
||||
|
||||
def emit_delay(cycles):
|
||||
# 4 cycles per iteration (2x nop)
|
||||
for i in range(cycles // 4):
|
||||
print(" nop")
|
||||
print(" nop")
|
||||
remainder = cycles % 4
|
||||
if remainder >= 3:
|
||||
print(" bit $ea")
|
||||
remainder -= 3
|
||||
# 2 cycles per nop
|
||||
for i in range(remainder // 2):
|
||||
print(" nop")
|
||||
ENDSCRIPT
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Second SCRIPT LIBRARY: Functions accumulate
|
||||
//-----------------------------------------------------------
|
||||
SCRIPT LIBRARY
|
||||
def emit_border_flash(color1, color2):
|
||||
print(" lda #%d" % color1)
|
||||
print(" sta $d020")
|
||||
print(" lda #%d" % color2)
|
||||
print(" sta $d020")
|
||||
|
||||
def emit_load_store(value, addr):
|
||||
print(" lda #%d" % value)
|
||||
print(" sta %d" % addr)
|
||||
ENDSCRIPT
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Main code using library functions
|
||||
//-----------------------------------------------------------
|
||||
LABEL start
|
||||
|
||||
// Use emit_nops from first library
|
||||
SCRIPT
|
||||
print("; 5 nops from library")
|
||||
emit_nops(5)
|
||||
ENDSCRIPT
|
||||
|
||||
// Use emit_delay from first library
|
||||
SCRIPT
|
||||
print("; 10 cycle delay")
|
||||
emit_delay(10)
|
||||
ENDSCRIPT
|
||||
|
||||
// Use emit_border_flash from second library
|
||||
SCRIPT
|
||||
print("; border flash red/blue")
|
||||
emit_border_flash(2, 6)
|
||||
ENDSCRIPT
|
||||
|
||||
// Combine multiple library functions
|
||||
SCRIPT
|
||||
print("; combined: delay + flash + nops")
|
||||
emit_delay(8)
|
||||
emit_border_flash(0, 1)
|
||||
emit_nops(3)
|
||||
ENDSCRIPT
|
||||
|
||||
// Use emit_load_store
|
||||
SCRIPT
|
||||
print("; load/store to border")
|
||||
emit_load_store(5, 0xd020)
|
||||
ENDSCRIPT
|
||||
|
||||
SUBEND
|
||||
1
examples/script_library_demo/start_in_vice.sh
Normal file
1
examples/script_library_demo/start_in_vice.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
x64 -autostartprgmode 1 main.prg
|
||||
|
|
@ -37,19 +37,24 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
|
|||
var codeOutput []string
|
||||
var lastKind = preproc.Source
|
||||
var scriptBuffer []string
|
||||
var scriptIsLibrary bool
|
||||
|
||||
for i, line := range lines {
|
||||
// Detect kind transitions and emit markers
|
||||
if line.Kind != lastKind {
|
||||
// Execute and close previous Script block
|
||||
if lastKind == preproc.Script {
|
||||
scriptOutput, err := executeScript(scriptBuffer, c.ctx)
|
||||
// Execute and close previous Script or ScriptLibrary block
|
||||
if lastKind == preproc.Script || lastKind == preproc.ScriptLibrary {
|
||||
scriptOutput, err := executeScript(scriptBuffer, c.ctx, scriptIsLibrary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("script execution failed: %w", err)
|
||||
}
|
||||
codeOutput = append(codeOutput, scriptOutput...)
|
||||
scriptBuffer = nil
|
||||
codeOutput = append(codeOutput, "; ENDSCRIPT")
|
||||
if scriptIsLibrary {
|
||||
codeOutput = append(codeOutput, "; ENDSCRIPT LIBRARY")
|
||||
} else {
|
||||
codeOutput = append(codeOutput, "; ENDSCRIPT")
|
||||
}
|
||||
}
|
||||
|
||||
// Close previous Assembler block
|
||||
|
|
@ -62,6 +67,10 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
|
|||
codeOutput = append(codeOutput, "; ASM")
|
||||
} else if line.Kind == preproc.Script {
|
||||
codeOutput = append(codeOutput, "; SCRIPT")
|
||||
scriptIsLibrary = false
|
||||
} else if line.Kind == preproc.ScriptLibrary {
|
||||
codeOutput = append(codeOutput, "; SCRIPT LIBRARY")
|
||||
scriptIsLibrary = true
|
||||
}
|
||||
|
||||
lastKind = line.Kind
|
||||
|
|
@ -89,7 +98,7 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
|
|||
text = text[:start] + expandedName + text[end+1:]
|
||||
}
|
||||
codeOutput = append(codeOutput, text)
|
||||
} else if line.Kind == preproc.Script {
|
||||
} else if line.Kind == preproc.Script || line.Kind == preproc.ScriptLibrary {
|
||||
// Collect script lines for execution
|
||||
scriptBuffer = append(scriptBuffer, line.Text)
|
||||
}
|
||||
|
|
@ -127,11 +136,11 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
|
|||
|
||||
// Close any open block
|
||||
if lastKind == preproc.Assembler {
|
||||
//codeOutput = append(codeOutput, "; ENDASM")
|
||||
return nil, fmt.Errorf("Unclosed ASM block.")
|
||||
} else if lastKind == preproc.Script {
|
||||
//codeOutput = append(codeOutput, "; ENDSCRIPT")
|
||||
return nil, fmt.Errorf("Unclosed SCRIPT block.")
|
||||
} else if lastKind == preproc.ScriptLibrary {
|
||||
return nil, fmt.Errorf("Unclosed SCRIPT LIBRARY block.")
|
||||
}
|
||||
|
||||
// Analyze for overlapping absolute addresses in function call chains
|
||||
|
|
|
|||
|
|
@ -139,6 +139,9 @@ func TestCompilerContext(t *testing.T) {
|
|||
if ctx.Pragma == nil {
|
||||
t.Error("Pragma not initialized")
|
||||
}
|
||||
if ctx.ScriptLibraryGlobals == nil {
|
||||
t.Error("ScriptLibraryGlobals not initialized")
|
||||
}
|
||||
|
||||
// Test CurrentScope
|
||||
scope := ctx.CurrentScope()
|
||||
|
|
@ -146,3 +149,184 @@ func TestCompilerContext(t *testing.T) {
|
|||
t.Errorf("Expected nil scope in global context, got %v", scope)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteScript_BasicPrint(t *testing.T) {
|
||||
pragma := preproc.NewPragma()
|
||||
ctx := NewCompilerContext(pragma)
|
||||
|
||||
scriptLines := []string{
|
||||
"for i in range(3):",
|
||||
" print(' nop')",
|
||||
}
|
||||
|
||||
output, err := executeScript(scriptLines, ctx, false)
|
||||
if err != nil {
|
||||
t.Fatalf("executeScript failed: %v", err)
|
||||
}
|
||||
|
||||
if len(output) != 3 {
|
||||
t.Fatalf("expected 3 output lines, got %d: %v", len(output), output)
|
||||
}
|
||||
|
||||
for i, line := range output {
|
||||
if line != " nop" {
|
||||
t.Errorf("line %d: expected ' nop', got %q", i, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteScript_EmptyOutput(t *testing.T) {
|
||||
pragma := preproc.NewPragma()
|
||||
ctx := NewCompilerContext(pragma)
|
||||
|
||||
scriptLines := []string{
|
||||
"x = 1 + 1",
|
||||
}
|
||||
|
||||
output, err := executeScript(scriptLines, ctx, false)
|
||||
if err != nil {
|
||||
t.Fatalf("executeScript failed: %v", err)
|
||||
}
|
||||
|
||||
if len(output) != 0 {
|
||||
t.Errorf("expected 0 output lines, got %d: %v", len(output), output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteScript_Library_DefinesFunction(t *testing.T) {
|
||||
pragma := preproc.NewPragma()
|
||||
ctx := NewCompilerContext(pragma)
|
||||
|
||||
// Define a function in library mode
|
||||
libraryLines := []string{
|
||||
"def emit_nops(count):",
|
||||
" for i in range(count):",
|
||||
" print(' nop')",
|
||||
}
|
||||
|
||||
_, err := executeScript(libraryLines, ctx, true)
|
||||
if err != nil {
|
||||
t.Fatalf("library executeScript failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify function is in globals
|
||||
if _, ok := ctx.ScriptLibraryGlobals["emit_nops"]; !ok {
|
||||
t.Fatal("expected emit_nops to be defined in ScriptLibraryGlobals")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteScript_Library_FunctionCallableFromScript(t *testing.T) {
|
||||
pragma := preproc.NewPragma()
|
||||
ctx := NewCompilerContext(pragma)
|
||||
|
||||
// First: define function in library
|
||||
libraryLines := []string{
|
||||
"def emit_nops(count):",
|
||||
" for i in range(count):",
|
||||
" print(' nop')",
|
||||
}
|
||||
|
||||
_, err := executeScript(libraryLines, ctx, true)
|
||||
if err != nil {
|
||||
t.Fatalf("library executeScript failed: %v", err)
|
||||
}
|
||||
|
||||
// Second: call function from regular script
|
||||
scriptLines := []string{
|
||||
"emit_nops(2)",
|
||||
}
|
||||
|
||||
output, err := executeScript(scriptLines, ctx, false)
|
||||
if err != nil {
|
||||
t.Fatalf("script executeScript failed: %v", err)
|
||||
}
|
||||
|
||||
if len(output) != 2 {
|
||||
t.Fatalf("expected 2 output lines, got %d: %v", len(output), output)
|
||||
}
|
||||
|
||||
for i, line := range output {
|
||||
if line != " nop" {
|
||||
t.Errorf("line %d: expected ' nop', got %q", i, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteScript_MultipleLibraries_Accumulate(t *testing.T) {
|
||||
pragma := preproc.NewPragma()
|
||||
ctx := NewCompilerContext(pragma)
|
||||
|
||||
// First library: define func_a
|
||||
lib1 := []string{
|
||||
"def func_a():",
|
||||
" print(' ; from a')",
|
||||
}
|
||||
_, err := executeScript(lib1, ctx, true)
|
||||
if err != nil {
|
||||
t.Fatalf("lib1 failed: %v", err)
|
||||
}
|
||||
|
||||
// Second library: define func_b (should still have func_a)
|
||||
lib2 := []string{
|
||||
"def func_b():",
|
||||
" print(' ; from b')",
|
||||
}
|
||||
_, err = executeScript(lib2, ctx, true)
|
||||
if err != nil {
|
||||
t.Fatalf("lib2 failed: %v", err)
|
||||
}
|
||||
|
||||
// Both functions should be available
|
||||
if _, ok := ctx.ScriptLibraryGlobals["func_a"]; !ok {
|
||||
t.Error("func_a missing after second library")
|
||||
}
|
||||
if _, ok := ctx.ScriptLibraryGlobals["func_b"]; !ok {
|
||||
t.Error("func_b missing after second library")
|
||||
}
|
||||
|
||||
// Call both from a script
|
||||
scriptLines := []string{
|
||||
"func_a()",
|
||||
"func_b()",
|
||||
}
|
||||
output, err := executeScript(scriptLines, ctx, false)
|
||||
if err != nil {
|
||||
t.Fatalf("script failed: %v", err)
|
||||
}
|
||||
|
||||
if len(output) != 2 {
|
||||
t.Fatalf("expected 2 lines, got %d: %v", len(output), output)
|
||||
}
|
||||
if output[0] != " ; from a" {
|
||||
t.Errorf("expected ' ; from a', got %q", output[0])
|
||||
}
|
||||
if output[1] != " ; from b" {
|
||||
t.Errorf("expected ' ; from b', got %q", output[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteScript_RegularScript_DoesNotPersist(t *testing.T) {
|
||||
pragma := preproc.NewPragma()
|
||||
ctx := NewCompilerContext(pragma)
|
||||
|
||||
// Define function in regular script (not library)
|
||||
scriptLines := []string{
|
||||
"def local_func():",
|
||||
" print('hello')",
|
||||
"local_func()",
|
||||
}
|
||||
|
||||
output, err := executeScript(scriptLines, ctx, false)
|
||||
if err != nil {
|
||||
t.Fatalf("script failed: %v", err)
|
||||
}
|
||||
|
||||
if len(output) != 1 || output[0] != "hello" {
|
||||
t.Errorf("unexpected output: %v", output)
|
||||
}
|
||||
|
||||
// Function should NOT be in globals (it was in regular script)
|
||||
if _, ok := ctx.ScriptLibraryGlobals["local_func"]; ok {
|
||||
t.Error("local_func should not persist from regular script")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package compiler
|
|||
|
||||
import (
|
||||
"c65gm/internal/preproc"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
// CompilerContext holds all shared resources needed by commands during compilation
|
||||
|
|
@ -26,6 +28,9 @@ type CompilerContext struct {
|
|||
|
||||
// Pragma access for per-line pragma lookup
|
||||
Pragma *preproc.Pragma
|
||||
|
||||
// ScriptLibraryGlobals holds persisted Starlark globals from SCRIPT LIBRARY blocks
|
||||
ScriptLibraryGlobals starlark.StringDict
|
||||
}
|
||||
|
||||
// NewCompilerContext creates a new compiler context with initialized resources
|
||||
|
|
@ -35,16 +40,17 @@ func NewCompilerContext(pragma *preproc.Pragma) *CompilerContext {
|
|||
generalStack := NewLabelStack("_L")
|
||||
|
||||
ctx := &CompilerContext{
|
||||
SymbolTable: symTable,
|
||||
ConstStrHandler: constStrHandler,
|
||||
LoopStartStack: NewLabelStack("_LOOPSTART"),
|
||||
LoopEndStack: NewLabelStack("_LOOPEND"),
|
||||
IfStack: NewLabelStack("_I"),
|
||||
GeneralStack: generalStack,
|
||||
ForStack: NewForStack(),
|
||||
SwitchStack: NewSwitchStack(),
|
||||
CaseSkipStack: NewLabelStack("_SKIPCASE"),
|
||||
Pragma: pragma,
|
||||
SymbolTable: symTable,
|
||||
ConstStrHandler: constStrHandler,
|
||||
LoopStartStack: NewLabelStack("_LOOPSTART"),
|
||||
LoopEndStack: NewLabelStack("_LOOPEND"),
|
||||
IfStack: NewLabelStack("_I"),
|
||||
GeneralStack: generalStack,
|
||||
ForStack: NewForStack(),
|
||||
SwitchStack: NewSwitchStack(),
|
||||
CaseSkipStack: NewLabelStack("_SKIPCASE"),
|
||||
Pragma: pragma,
|
||||
ScriptLibraryGlobals: make(starlark.StringDict),
|
||||
}
|
||||
|
||||
// FunctionHandler needs references to other components
|
||||
|
|
|
|||
|
|
@ -8,20 +8,28 @@ import (
|
|||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
// executeScript runs a Starlark script and returns the output lines
|
||||
func executeScript(scriptLines []string, ctx *CompilerContext) ([]string, error) {
|
||||
// executeScript runs a Starlark script and returns the output lines.
|
||||
// If isLibrary is true, the script is executed at top level (no _main wrapper)
|
||||
// and resulting globals are persisted to ctx.ScriptLibraryGlobals.
|
||||
func executeScript(scriptLines []string, ctx *CompilerContext, isLibrary bool) ([]string, error) {
|
||||
// Join script lines
|
||||
scriptText := strings.Join(scriptLines, "\n")
|
||||
|
||||
// Expand |varname| -> actual variable names
|
||||
scriptText = expandVariables(scriptText, ctx)
|
||||
|
||||
// Wrap in function (Starlark requires control flow inside functions)
|
||||
wrappedScript := "def _main():\n"
|
||||
for _, line := range strings.Split(scriptText, "\n") {
|
||||
wrappedScript += " " + line + "\n"
|
||||
var finalScript string
|
||||
if isLibrary {
|
||||
// LIBRARY: execute at top level so defs become globals
|
||||
finalScript = scriptText
|
||||
} else {
|
||||
// Regular SCRIPT: wrap in function (Starlark requires control flow inside functions)
|
||||
finalScript = "def _main():\n"
|
||||
for _, line := range strings.Split(scriptText, "\n") {
|
||||
finalScript += " " + line + "\n"
|
||||
}
|
||||
finalScript += "_main()\n"
|
||||
}
|
||||
wrappedScript += "_main()\n"
|
||||
|
||||
// Capture print output
|
||||
var output bytes.Buffer
|
||||
|
|
@ -35,17 +43,27 @@ func executeScript(scriptLines []string, ctx *CompilerContext) ([]string, error)
|
|||
// Set execution limit (prevent infinite loops)
|
||||
thread.SetMaxExecutionSteps(1000000) // 1M steps
|
||||
|
||||
// Predeclared functions (math module)
|
||||
// Build predeclared: math module + library globals
|
||||
predeclared := starlark.StringDict{
|
||||
"math": math.Module,
|
||||
}
|
||||
for k, v := range ctx.ScriptLibraryGlobals {
|
||||
predeclared[k] = v
|
||||
}
|
||||
|
||||
// Execute
|
||||
_, err := starlark.ExecFile(thread, "script.star", wrappedScript, predeclared)
|
||||
globals, err := starlark.ExecFile(thread, "script.star", finalScript, predeclared)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For LIBRARY: persist new globals (functions, variables defined at top level)
|
||||
if isLibrary {
|
||||
for k, v := range globals {
|
||||
ctx.ScriptLibraryGlobals[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Split output into lines for assembly
|
||||
outputStr := output.String()
|
||||
if outputStr == "" {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const (
|
|||
Source LineKind = iota
|
||||
Assembler
|
||||
Script
|
||||
ScriptLibrary
|
||||
)
|
||||
|
||||
// Line represents one post-processed source line and its provenance.
|
||||
|
|
@ -46,12 +47,13 @@ func PreProcess(rootFilename string, reader ...FileReader) ([]Line, *Pragma, err
|
|||
// -------------------- internal --------------------
|
||||
|
||||
type preproc struct {
|
||||
defs *DefineList // from definelist.go
|
||||
pragma *Pragma // pragma handler
|
||||
cond []bool // conditional stack; a line is active if all are true
|
||||
inAsm bool // true when inside ASM/ENDASM block
|
||||
inScript bool // true when inside SCRIPT/ENDSCRIPT block
|
||||
reader FileReader // file reader abstraction
|
||||
defs *DefineList // from definelist.go
|
||||
pragma *Pragma // pragma handler
|
||||
cond []bool // conditional stack; a line is active if all are true
|
||||
inAsm bool // true when inside ASM/ENDASM block
|
||||
inScript bool // true when inside SCRIPT/ENDSCRIPT block
|
||||
inScriptLibrary bool // true when inside SCRIPT LIBRARY/ENDSCRIPT block
|
||||
reader FileReader // file reader abstraction
|
||||
}
|
||||
|
||||
func newPreproc(reader FileReader) *preproc {
|
||||
|
|
@ -116,15 +118,19 @@ func (p *preproc) run(root string) ([]Line, error) {
|
|||
tokens := strings.Fields(raw)
|
||||
|
||||
// ASM mode handling
|
||||
if !p.inAsm && !p.inScript {
|
||||
if !p.inAsm && !p.inScript && !p.inScriptLibrary {
|
||||
// Check for ASM entry
|
||||
if includeSource && len(tokens) > 0 && tokens[0] == "ASM" {
|
||||
p.inAsm = true
|
||||
continue // don't emit ASM marker
|
||||
}
|
||||
// Check for SCRIPT entry
|
||||
// Check for SCRIPT entry (SCRIPT LIBRARY or plain SCRIPT)
|
||||
if includeSource && len(tokens) > 0 && tokens[0] == "SCRIPT" {
|
||||
p.inScript = true
|
||||
if len(tokens) > 1 && tokens[1] == "LIBRARY" {
|
||||
p.inScriptLibrary = true
|
||||
} else {
|
||||
p.inScript = true
|
||||
}
|
||||
continue // don't emit SCRIPT marker
|
||||
}
|
||||
} else if p.inAsm {
|
||||
|
|
@ -144,20 +150,26 @@ func (p *preproc) run(root string) ([]Line, error) {
|
|||
PragmaSetIndex: p.pragma.GetCurrentPragmaSetIndex(),
|
||||
})
|
||||
continue
|
||||
} else if p.inScript {
|
||||
// We're in SCRIPT mode
|
||||
} else if p.inScript || p.inScriptLibrary {
|
||||
// We're in SCRIPT or SCRIPT LIBRARY mode
|
||||
// Check for ENDSCRIPT
|
||||
if len(tokens) > 0 && tokens[0] == "ENDSCRIPT" {
|
||||
p.inScript = false
|
||||
p.inScriptLibrary = false
|
||||
continue // don't emit ENDSCRIPT marker
|
||||
}
|
||||
// Otherwise emit line verbatim as Script
|
||||
// Determine the kind based on which mode we're in
|
||||
kind := Script
|
||||
if p.inScriptLibrary {
|
||||
kind = ScriptLibrary
|
||||
}
|
||||
// Emit line verbatim with appropriate kind
|
||||
out = append(out, Line{
|
||||
RawText: raw,
|
||||
Text: raw,
|
||||
Filename: currFrame.path,
|
||||
LineNo: currFrame.line,
|
||||
Kind: Script,
|
||||
Kind: kind,
|
||||
PragmaSetIndex: p.pragma.GetCurrentPragmaSetIndex(),
|
||||
})
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -947,6 +947,80 @@ func TestPreProcess_EmptyScriptBlock(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPreProcess_ScriptLibraryBlock(t *testing.T) {
|
||||
files := map[string][]string{
|
||||
"test.c65": {
|
||||
"SCRIPT LIBRARY",
|
||||
"def my_func():",
|
||||
" print('nop')",
|
||||
"ENDSCRIPT",
|
||||
"NOP",
|
||||
},
|
||||
}
|
||||
reader := NewMockFileReader(files)
|
||||
lines, _, err := PreProcess("test.c65", reader)
|
||||
if err != nil {
|
||||
t.Fatalf("PreProcess failed: %v", err)
|
||||
}
|
||||
|
||||
// Should have 2 script lines + 1 source line
|
||||
if len(lines) != 3 {
|
||||
t.Fatalf("expected 3 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
// Script library lines should have ScriptLibrary kind
|
||||
if lines[0].Kind != ScriptLibrary {
|
||||
t.Errorf("expected Kind=ScriptLibrary, got %v", lines[0].Kind)
|
||||
}
|
||||
if lines[0].Text != "def my_func():" {
|
||||
t.Errorf("expected 'def my_func():', got %q", lines[0].Text)
|
||||
}
|
||||
|
||||
if lines[1].Kind != ScriptLibrary {
|
||||
t.Errorf("expected Kind=ScriptLibrary, got %v", lines[1].Kind)
|
||||
}
|
||||
|
||||
// Source line after ENDSCRIPT
|
||||
if lines[2].Kind != Source {
|
||||
t.Errorf("expected Kind=Source, got %v", lines[2].Kind)
|
||||
}
|
||||
if lines[2].Text != "NOP" {
|
||||
t.Errorf("expected 'NOP', got %q", lines[2].Text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreProcess_ScriptVsScriptLibrary(t *testing.T) {
|
||||
files := map[string][]string{
|
||||
"test.c65": {
|
||||
"SCRIPT LIBRARY",
|
||||
"def foo(): pass",
|
||||
"ENDSCRIPT",
|
||||
"SCRIPT",
|
||||
"foo()",
|
||||
"ENDSCRIPT",
|
||||
},
|
||||
}
|
||||
reader := NewMockFileReader(files)
|
||||
lines, _, err := PreProcess("test.c65", reader)
|
||||
if err != nil {
|
||||
t.Fatalf("PreProcess failed: %v", err)
|
||||
}
|
||||
|
||||
if len(lines) != 2 {
|
||||
t.Fatalf("expected 2 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
// First line is from SCRIPT LIBRARY
|
||||
if lines[0].Kind != ScriptLibrary {
|
||||
t.Errorf("line 0: expected ScriptLibrary, got %v", lines[0].Kind)
|
||||
}
|
||||
|
||||
// Second line is from regular SCRIPT
|
||||
if lines[1].Kind != Script {
|
||||
t.Errorf("line 1: expected Script, got %v", lines[1].Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreProcess_DollarEscapeExpansion(t *testing.T) {
|
||||
files := map[string][]string{
|
||||
"test.c65": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue