diff --git a/.run/Demo1.run.xml b/.run/Demo1.run.xml
index 98e3db5b7119652b8849bf799961021dff7b6674..42b22c431b17ae38d828ebce96512bfe55423154 100644
--- a/.run/Demo1.run.xml
+++ b/.run/Demo1.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo10.run.xml b/.run/Demo10.run.xml
index 29e014fd14c525d7e942f77e59eded6e76603693..45a30820b9fc2cf9aa418a8e00c32605469dc92d 100644
--- a/.run/Demo10.run.xml
+++ b/.run/Demo10.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo11.run.xml b/.run/Demo11.run.xml
index 6888bedd44083a8c6652903017a586b5db7bf147..a063d418504d16e2ac6dcef0c238a9e0555ad5fb 100644
--- a/.run/Demo11.run.xml
+++ b/.run/Demo11.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo12.run.xml b/.run/Demo12.run.xml
index 844a988a21983ac5b2a4e3ccba0d6da2394b8200..784a031ec7ad0032eab53c244e686dbd2feff973 100644
--- a/.run/Demo12.run.xml
+++ b/.run/Demo12.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo13.run.xml b/.run/Demo13.run.xml
index e6dff330e1c228bfa225f07190731e6440ef4eba..2544ad6a0ba07a12843b6248653021b2eea5bd43 100644
--- a/.run/Demo13.run.xml
+++ b/.run/Demo13.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo15.run.xml b/.run/Demo15.run.xml
index 17675600b320dc47e70fb162068d949c61480894..50a12666093f527847dbd8613d8cab9566f06be2 100644
--- a/.run/Demo15.run.xml
+++ b/.run/Demo15.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo16.run.xml b/.run/Demo16.run.xml
index f2176256802c2b74c57bfe0a39df6bf85f2058ac..446dd4fb0e46d757b7c453bc8db50b8b471ceae8 100644
--- a/.run/Demo16.run.xml
+++ b/.run/Demo16.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo17.run.xml b/.run/Demo17.run.xml
index c608651dca7564c77f6fc9214d64b014eb857591..f4e093d4ffda465fc2b48e60858649cb40c133f1 100644
--- a/.run/Demo17.run.xml
+++ b/.run/Demo17.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo18.run.xml b/.run/Demo18.run.xml
index c9f4bb629b40513c8318817d996c2a85b4f936af..f028a09ba14e3b4f7d9b5c342bbaa5ffea5dab42 100644
--- a/.run/Demo18.run.xml
+++ b/.run/Demo18.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo19.run.xml b/.run/Demo19.run.xml
index 0896751cd1f356fce30ba1897c518b157fb36841..860efbffc26951eb7a995a3a2e14869109815a6c 100644
--- a/.run/Demo19.run.xml
+++ b/.run/Demo19.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo2.run.xml b/.run/Demo2.run.xml
index 4fb0f1715f326580bd75a4b22ce4ac5f7aafdc3d..a4fde6df43a5d81dd95ff4448c4d44eac3e9238b 100644
--- a/.run/Demo2.run.xml
+++ b/.run/Demo2.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo20.run.xml b/.run/Demo20.run.xml
index bdf690b12677f5ade58d404baad2b8047502f747..da12a47cfaf2be6e91996dad0d1214678f71af33 100644
--- a/.run/Demo20.run.xml
+++ b/.run/Demo20.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo21.run.xml b/.run/Demo21.run.xml
index 2076c8c96c55e25115d9b357e279a9c1e8032642..dfac5dc9a39aff39323a263fb039aefb0d8ca2b5 100644
--- a/.run/Demo21.run.xml
+++ b/.run/Demo21.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo23.run.xml b/.run/Demo23.run.xml
index 43e35ba1d0867288775a5fd592a1269acf03cf12..31be367f0f0875be8a0b3b33e5c3b04e92d7d980 100644
--- a/.run/Demo23.run.xml
+++ b/.run/Demo23.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/Demo26.run.xml b/.run/Demo26.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fcca4f51030dfe0bf41e31cb966ecba5460ca694
--- /dev/null
+++ b/.run/Demo26.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo27.run.xml b/.run/Demo27.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..77a6ada2786940f84fd943fbd81d4f6f121821da
--- /dev/null
+++ b/.run/Demo27.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo28.run.xml b/.run/Demo28.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..37840ef2f6086f30f7e03bf8385a2aa373eaf947
--- /dev/null
+++ b/.run/Demo28.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo29.run.xml b/.run/Demo29.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bf8322b814e0d6bfcb3f13891123a61847d2dc6b
--- /dev/null
+++ b/.run/Demo29.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo3.run.xml b/.run/Demo3.run.xml
index 0bf617235b723aba988d31a7fcdb0ca13b3513da..925a855c49091f6e0ed5258fd8e4e9a9ebca80a6 100644
--- a/.run/Demo3.run.xml
+++ b/.run/Demo3.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo30.run.xml b/.run/Demo30.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bdda8eb11b992784217004a1fb8765bfa738ddb7
--- /dev/null
+++ b/.run/Demo30.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo31.run.xml b/.run/Demo31.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4caae0dbbcef8272148315f15cd7aae09eafb209
--- /dev/null
+++ b/.run/Demo31.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo32.run.xml b/.run/Demo32.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..93f71ca13872993f3d0400f1938e0fff1cc91e10
--- /dev/null
+++ b/.run/Demo32.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo33.run.xml b/.run/Demo33.run.xml
new file mode 100644
index 0000000000000000000000000000000000000000..971ad410781ec70fcb00ae06b12088fe35225c30
--- /dev/null
+++ b/.run/Demo33.run.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Demo4.run.xml b/.run/Demo4.run.xml
index d77722463bece0eb3da484a0ec4b34671dd3bd22..d3614dc5aa523c716bc2b4bc759503a3b9f95799 100644
--- a/.run/Demo4.run.xml
+++ b/.run/Demo4.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo5.run.xml b/.run/Demo5.run.xml
index 3fa8a5670e2303e4d9a4de7b30e2e48a930131df..234995d0bbb85b11be3a6dc4eb81d21056ca305d 100644
--- a/.run/Demo5.run.xml
+++ b/.run/Demo5.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo6.run.xml b/.run/Demo6.run.xml
index a5c343224a7997e7c155faba588fa5ed39cca80c..2f425b94c720246487625bac3e9f7c3939ee5f35 100644
--- a/.run/Demo6.run.xml
+++ b/.run/Demo6.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo7.run.xml b/.run/Demo7.run.xml
index bef0dcede2a6ccf22c3f7d48ca9816c17c22fd85..b8828cb47986a9e09ca60c277a54f2f41ced5803 100644
--- a/.run/Demo7.run.xml
+++ b/.run/Demo7.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo8.run.xml b/.run/Demo8.run.xml
index c14026b7384d200a4bcacb911c5bcbff2a4c2044..ea768613c0322a0e86f9803f4d3bcf20d394010c 100644
--- a/.run/Demo8.run.xml
+++ b/.run/Demo8.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.run/Demo9.run.xml b/.run/Demo9.run.xml
index c8036a067cce40f68cb22c7fb047f3dfc1b583aa..a542ab5ecfc6538c34b4e2c0b0e22ba6b83ec834 100644
--- a/.run/Demo9.run.xml
+++ b/.run/Demo9.run.xml
@@ -3,7 +3,7 @@
-
+
diff --git "a/.run/\346\265\213\350\257\225.run.xml" "b/.run/\346\265\213\350\257\225.run.xml"
index b673e11f85d375772d038f84b2c80a2571e41c52..8152d4dc45667ec45b249537f0c64068ec33db49 100644
--- "a/.run/\346\265\213\350\257\225.run.xml"
+++ "b/.run/\346\265\213\350\257\225.run.xml"
@@ -26,4 +26,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/playground/Demo/Demo1/Main.snow b/playground/Demo/Demo1/Main.snow
index 090e5ac91c0f92402859c2b7934b34856afee34c..ac500bce8d4d03eb34bcbaf7a438934db775c4c6 100644
--- a/playground/Demo/Demo1/Main.snow
+++ b/playground/Demo/Demo1/Main.snow
@@ -1,10 +1,9 @@
module: Main
- import:Math
+ import:Math,os
function: main
- returns: int
+ returns: void
body:
- Math.add(6,1)
- return 0
+ os.print(Math.add(6,1))
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo1/OS.snow b/playground/Demo/Demo1/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo1/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo10/Main.snow b/playground/Demo/Demo10/Main.snow
index c5ee5953e3721668130351e75a047a2f39d0faa2..f314bbb9b3114c2ce03185242e045897d4b5f511 100644
--- a/playground/Demo/Demo10/Main.snow
+++ b/playground/Demo/Demo10/Main.snow
@@ -1,10 +1,13 @@
-function: main
- returns: int
- body:
- declare res: boolean = 8L > 7L
- if res then
- return 131
- end if
- return 65537
- end body
-end function
\ No newline at end of file
+module : Main
+ import:os
+ function: main
+ returns: void
+ body:
+ declare res: boolean = 8L > 9L
+ if res then
+ os.println(131)
+ end if
+ os.println(65537)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo10/OS.snow b/playground/Demo/Demo10/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo10/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo11/Main.snow b/playground/Demo/Demo11/Main.snow
index a64b5c8292798feae9a12e8c916e22b6830fa38d..7a402574826335a7301ed450f0187c889c30eba9 100644
--- a/playground/Demo/Demo11/Main.snow
+++ b/playground/Demo/Demo11/Main.snow
@@ -1,6 +1,24 @@
function: main
- returns: int
+ returns: void
body:
- return 65537
+ print(1)
+ end body
+end function
+
+function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+end function
+
+function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
\ No newline at end of file
diff --git a/playground/Demo/Demo12/Main.snow b/playground/Demo/Demo12/Main.snow
index cadccb582f5b48daaed6e224616b179bad4fdef6..8b1abe2aebb37a6bfb743a6294b456e12732fa29 100644
--- a/playground/Demo/Demo12/Main.snow
+++ b/playground/Demo/Demo12/Main.snow
@@ -1,10 +1,9 @@
module: Main
+ import:os
function: main
- returns: int
+ returns: void
body:
- foo()
-
- return 0
+ os.print(foo())
end body
end function
diff --git a/playground/Demo/Demo12/OS.snow b/playground/Demo/Demo12/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo12/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo13/OS.snow b/playground/Demo/Demo13/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo13/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo14/OS.snow b/playground/Demo/Demo14/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo14/OS.snow
+++ b/playground/Demo/Demo14/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo15/Main.snow b/playground/Demo/Demo15/Main.snow
index 3ddb8b4aefa4382d4b7bdf76b044677c195b3c55..f670bb93b94330ffb2267cc132cb830a42adb9c3 100644
--- a/playground/Demo/Demo15/Main.snow
+++ b/playground/Demo/Demo15/Main.snow
@@ -13,7 +13,7 @@ module: Main
function: abc
returns: int
body:
- return 1
+ return 1 +num2
end body
end function
diff --git a/playground/Demo/Demo15/OS.snow b/playground/Demo/Demo15/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo15/OS.snow
+++ b/playground/Demo/Demo15/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo16/OS.snow b/playground/Demo/Demo16/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo16/OS.snow
+++ b/playground/Demo/Demo16/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo17/OS.snow b/playground/Demo/Demo17/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo17/OS.snow
+++ b/playground/Demo/Demo17/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo18/OS.snow b/playground/Demo/Demo18/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo18/OS.snow
+++ b/playground/Demo/Demo18/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo19/OS.snow b/playground/Demo/Demo19/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo19/OS.snow
+++ b/playground/Demo/Demo19/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo2/Main.snow b/playground/Demo/Demo2/Main.snow
index a0f1396acb16b4956ef60141818a95cea4abdaba..fc6f4f04adfb659563e2e32ed1db9c3399efb909 100644
--- a/playground/Demo/Demo2/Main.snow
+++ b/playground/Demo/Demo2/Main.snow
@@ -1,9 +1,10 @@
module: Main
+ import : os
function: main
params:
- returns: int
+ returns: void
body:
- return (1+2) / 3 * 4 + 2 *2
+ os.print((1+2) / 3 * 4 + 2 *2)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo2/OS.snow b/playground/Demo/Demo2/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo2/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo20/OS.snow b/playground/Demo/Demo20/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo20/OS.snow
+++ b/playground/Demo/Demo20/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo21/OS.snow b/playground/Demo/Demo21/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo21/OS.snow
+++ b/playground/Demo/Demo21/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo22/OS.snow b/playground/Demo/Demo22/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo22/OS.snow
+++ b/playground/Demo/Demo22/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo23/OS.snow b/playground/Demo/Demo23/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo23/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo24/OS.snow b/playground/Demo/Demo24/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo24/OS.snow
+++ b/playground/Demo/Demo24/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo25/OS.snow b/playground/Demo/Demo25/OS.snow
index 1982627d5eff2542e01f927ea96286f870643025..69dbb1ef755310a8a5e754b734056c25bd8d991c 100644
--- a/playground/Demo/Demo25/OS.snow
+++ b/playground/Demo/Demo25/OS.snow
@@ -1,11 +1,18 @@
module: os
- import: os
function: print
params:
declare i1: int
returns: void
body:
- syscall("PRINT",i1)
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo26/OS.snow b/playground/Demo/Demo26/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo26/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo27/OS.snow b/playground/Demo/Demo27/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo27/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo27/Test1.snow b/playground/Demo/Demo27/Test1.snow
new file mode 100644
index 0000000000000000000000000000000000000000..9f4233e91e693b311c1be2ca69712b1fe0bdfa67
--- /dev/null
+++ b/playground/Demo/Demo27/Test1.snow
@@ -0,0 +1,74 @@
+// 定义模块
+module: Main
+ // 定义一个结构体 Counter
+ struct: Counter
+ fields:
+ declare value: int
+
+ // 构造方法
+ init:
+ params:
+ v: int
+ body:
+ this.value = v
+ end body
+ end init
+
+ // 方法:打印 value
+ function: printValue
+ returns: void
+ body:
+ os.print(this.value)
+ end body
+ end function
+
+ // 方法:打印 value + 100
+ function: printSum
+ returns: void
+ body:
+ os.print(this.value + 100)
+ end body
+ end function
+ end struct
+
+
+ // 定义 AdvancedCounter,继承 Counter
+ struct: AdvancedCounter extends Counter
+ fields:
+ declare offset: int
+
+ // 构造方法:调用父类构造(使用 init)
+ init:
+ params:
+ v: int
+ o: int
+ body:
+ super(v) // 调用父类构造方法
+ this.offset = o
+ end body
+ end init
+
+ // 重写 printSum:打印 value + offset
+ function: printSum
+ returns: void
+ body:
+ os.print(this.value + this.offset)
+ end body
+ end function
+ end struct
+
+
+ // 程序入口
+ function: main
+ returns: void
+ body:
+ declare c: Counter = new Counter(42)
+ c.printValue() // 打印 42
+ c.printSum() // 打印 142
+
+ declare ac: AdvancedCounter = new AdvancedCounter(42, 200)
+ ac.printValue() // 打印 42 (继承自父类)
+ ac.printSum() // 打印 242 (子类重写)
+ end body
+ end function
+end module
diff --git a/playground/Demo/Demo27/Test2.snow b/playground/Demo/Demo27/Test2.snow
new file mode 100644
index 0000000000000000000000000000000000000000..17613d6931da588cd059e85f77dec9042953e2fe
--- /dev/null
+++ b/playground/Demo/Demo27/Test2.snow
@@ -0,0 +1,135 @@
+// 演示 Snow OOP 四大要点:封装、继承、多态、重载
+module: Main
+ // 封装:基类 Animal
+ struct: Animal
+ fields:
+ declare name: string
+
+ init:
+ params:
+ n: string
+ body:
+ this.name = n
+ end body
+ end init
+
+ // 封装:提供只读访问器
+ function: getName
+ returns: string
+ body:
+ return this.name
+ end body
+ end function
+
+ // 多态:基类方法,默认虚
+ function: speak
+ returns: void
+ body:
+ os.print(this.name + " makes a sound.")
+ end body
+ end function
+
+ // 重载示例:同名 eat,不同参数
+ function: eat
+ params:
+ food: string
+ returns: void
+ body:
+ os.print(this.name + " eats " + food)
+ end body
+ end function
+
+ function: eat
+ params:
+ times: int
+ returns: void
+ body:
+ os.print(this.name + " eats " + times + " times.")
+ end body
+ end function
+ end struct
+
+
+ // 继承 + 多态:Dog
+ struct: Dog extends Animal
+ init:
+ params:
+ n: string
+ body:
+ super(n)
+ end body
+ end init
+
+ // 重写 speak
+ function: speak
+ returns: void
+ body:
+ os.print(this.name + " says: Woof!")
+ end body
+ end function
+ end struct
+
+
+ // 继承 + 多态:Cat
+ struct: Cat extends Animal
+ init:
+ params:
+ n: string
+ body:
+ super(n)
+ end body
+ end init
+
+ // 重写 speak
+ function: speak
+ returns: void
+ body:
+ os.print(this.name + " says: Meow!")
+ end body
+ end function
+ end struct
+
+
+ // 多态演示函数
+ function: letAnimalSpeak
+ params:
+ a: Animal
+ returns: void
+ body:
+ // 调用多态方法
+ a.speak()
+ end body
+ end function
+
+
+ // 程序入口
+ function: main
+ returns: void
+ body:
+ // 基类结构体
+ declare a: Animal = new Animal("GenericAnimal")
+ a.speak() // -> GenericAnimal makes a sound.
+ a.eat("food") // -> GenericAnimal eats food
+ a.eat(2) // -> GenericAnimal eats 2 times.
+
+ // 子结构体 Dog
+ declare d: Dog = new Dog("Buddy")
+ d.speak() // -> Buddy says: Woof!
+ d.eat("bone") // 调用基类 eat(String)
+ d.eat(3) // 调用基类 eat(int)
+
+ // 子类结构体 Cat
+ declare c: Cat = new Cat("Kitty")
+ c.speak() // -> Kitty says: Meow!
+ c.eat("fish")
+ c.eat(1)
+
+ // 多态:父类引用指向子类结构体
+ declare poly: Animal = new Dog("Max")
+ letAnimalSpeak(poly) // -> Max says: Woof!
+
+ poly = new Cat("Luna")
+ letAnimalSpeak(poly) // -> Luna says: Meow!
+ end body
+ end function
+end module
diff --git a/playground/Demo/Demo28/Main.snow b/playground/Demo/Demo28/Main.snow
new file mode 100644
index 0000000000000000000000000000000000000000..1b709c36bdc9f6c6ca134f7b1d77f48c79b8ee95
--- /dev/null
+++ b/playground/Demo/Demo28/Main.snow
@@ -0,0 +1,54 @@
+module: Main
+ import: os
+ // Animal结构体
+ struct: Animal
+ // 字段
+ fields:
+ declare name: string
+ declare age: int
+
+ // 构造函数
+ init:
+ params:
+ n: string
+ a: int
+ body:
+ this.name = n
+ this.age = a
+ end body
+ end init
+
+ function: getAge
+ returns: int
+ body:
+ // 获取字段 age
+ return this.age
+ end body
+ end function
+
+ function: setAge
+ params:
+ declare a: int
+ body:
+ // 设置字段 age
+ this.age = a
+ end body
+ end function
+ end struct
+
+ // 程序入口
+ function: main
+ returns: void
+ body:
+ // 实例化一个叫A的Animal,并且调用构造函数
+ declare a: Animal = new Animal("GenericAnimal", 1)
+ // 直接调用已导入模块内的静态函数
+ a.setAge(2)
+ os.println(a.getAge())
+ a.setAge(3)
+ os.println(a.getAge())
+
+
+ end body
+ end function
+end module
diff --git a/playground/Demo/Demo28/OS.snow b/playground/Demo/Demo28/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo28/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo29/Main.snow b/playground/Demo/Demo29/Main.snow
new file mode 100644
index 0000000000000000000000000000000000000000..458acc416907e87ff28d64fe0407d635656a0060
--- /dev/null
+++ b/playground/Demo/Demo29/Main.snow
@@ -0,0 +1,65 @@
+module: Main
+ import: os
+ struct: Address
+ fields:
+ declare country: int
+ declare city: int
+ init:
+ params:
+ country: int
+ city: int
+ body:
+ this.country = country
+ this.city = city
+ end body
+ end init
+
+ function: getCity
+ returns: int
+ body:
+ return this.city
+ end body
+ end function
+ end struct
+
+ struct: Person
+ fields:
+ declare name: int
+ declare addr: Address
+ init:
+ params:
+ name: int
+ addr: Address
+ body:
+ this.name = name
+ this.addr = addr
+ end body
+ end init
+
+ function: getName
+ returns: int
+ body:
+ return this.name
+ end body
+ end function
+
+ function: getAddr
+ returns: Address
+ body:
+ return this.addr
+ end body
+ end function
+ end struct
+
+ function: main
+ returns: void
+ body:
+ // 初始化 Person 和 Address 的实例
+ declare p: Person = new Person(123, new Address(1, 2))
+ os.println(p.getName()) // 打印 name
+ os.println(p.getAddr().getCity()) // 打印 city
+
+ end body
+ end function
+
+end module
diff --git a/playground/Demo/Demo29/OS.snow b/playground/Demo/Demo29/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo29/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo3/Main.snow b/playground/Demo/Demo3/Main.snow
index 6e9de2898f8516ce30f1333919adb4fd728646fb..d4ca6814e046af3d261414e534a15f2fb9eb43af 100644
--- a/playground/Demo/Demo3/Main.snow
+++ b/playground/Demo/Demo3/Main.snow
@@ -1,4 +1,5 @@
module: Main
+ import: os
function: main
params:
returns: int
@@ -11,6 +12,7 @@ module: Main
n3 =3
end if
end if
+ os.print(n3)
return n3
end body
end function
diff --git a/playground/Demo/Demo3/OS.snow b/playground/Demo/Demo3/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo3/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo30/Main.snow b/playground/Demo/Demo30/Main.snow
new file mode 100644
index 0000000000000000000000000000000000000000..fda54c3a66b0280d6c701722e3dd829117860348
--- /dev/null
+++ b/playground/Demo/Demo30/Main.snow
@@ -0,0 +1,66 @@
+module: Main
+ import: os
+ struct: Address
+ fields:
+ declare country: int
+ declare city: int
+ init:
+ params:
+ country: int
+ city: int
+ body:
+ this.country = country
+ this.city = city
+ end body
+ end init
+
+ function: getCity
+ returns: int
+ body:
+ return this.city
+ end body
+ end function
+ end struct
+
+ struct: Person
+ fields:
+ declare name: int
+ declare addr: Address
+ init:
+ params:
+ name: int
+ addr: Address
+ body:
+ this.name = name
+ this.addr = addr
+ end body
+ end init
+
+ function: getName
+ returns: int
+ body:
+ return this.name
+ end body
+ end function
+
+ function: getAddr
+ returns: Address
+ body:
+ return this.addr
+ end body
+ end function
+ end struct
+
+ function: main
+ returns: void
+ body:
+ // 初始化 Person 和 Address 的实例
+ declare p: Person = new Person(123, new Address(1, 2))
+ declare a: Person = p
+ os.println(a.getName()) // -> 123
+ os.println(a.getAddr().getCity()) // -> 2
+
+ end body
+ end function
+
+end module
diff --git a/playground/Demo/Demo30/OS.snow b/playground/Demo/Demo30/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo30/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo31/Main.snow b/playground/Demo/Demo31/Main.snow
new file mode 100644
index 0000000000000000000000000000000000000000..0d31c7f82c4ac704853a63fa5bfbf443f883d832
--- /dev/null
+++ b/playground/Demo/Demo31/Main.snow
@@ -0,0 +1,56 @@
+module: Main
+ import: os
+
+ // 父类 Person
+ struct: Person
+ fields:
+ declare name: int
+ init:
+ params:
+ name: int
+ body:
+ this.name = name
+ end body
+ end init
+
+ function: getName
+ returns: int
+ body:
+ return this.name
+ end body
+ end function
+ end struct
+
+ // 子类 Student 继承 Person
+ struct: Student extends Person
+ fields:
+ declare studentId: int
+ init:
+ params:
+ name: int
+ studentId: int
+ body:
+ super(name) // 调用父类构造函数
+ this.studentId = studentId
+ end body
+ end init
+
+ function: getStudentId
+ returns: int
+ body:
+ return this.studentId
+ end body
+ end function
+ end struct
+
+ function: main
+ returns: void
+ body:
+ declare s: Student = new Student(123, 1001)
+
+ // Student 拥有父类方法
+ os.println(s.getName()) // 来自 Person → 123
+ os.println(s.getStudentId()) // 来自 Student → 1001
+ end body
+ end function
+end module
diff --git a/playground/Demo/Demo31/OS.snow b/playground/Demo/Demo31/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo31/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo32/Main.snow b/playground/Demo/Demo32/Main.snow
new file mode 100644
index 0000000000000000000000000000000000000000..70312dd9bc969828b6ae6fe1c4255618d6ec2191
--- /dev/null
+++ b/playground/Demo/Demo32/Main.snow
@@ -0,0 +1,14 @@
+module: main
+ import: os,Student, Person
+
+ function: main
+ returns: void
+ body:
+ declare a: Student = new Student(1)
+
+ os.println(a.getName())
+
+
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo32/OS.snow b/playground/Demo/Demo32/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo32/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo32/Person.snow b/playground/Demo/Demo32/Person.snow
new file mode 100644
index 0000000000000000000000000000000000000000..395cd06a266e37be8bacef005baf936bc2932d42
--- /dev/null
+++ b/playground/Demo/Demo32/Person.snow
@@ -0,0 +1,20 @@
+module: Person
+struct:Person
+ fields:
+ declare name: int
+ init:
+ params:
+ declare name: int
+ body:
+ this.name = name
+ end body
+ end init
+
+ function: getName
+ returns: int
+ body:
+ return this.name
+ end body
+ end function
+end struct
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo32/Student.snow b/playground/Demo/Demo32/Student.snow
new file mode 100644
index 0000000000000000000000000000000000000000..a1cf41a44c03605d8528a3ca603d44f542db5778
--- /dev/null
+++ b/playground/Demo/Demo32/Student.snow
@@ -0,0 +1,24 @@
+module: Student
+import : Person
+
+struct : Student extends Person
+ fields:
+ declare id: int
+
+ init:
+ params:
+ declare name: int
+ body:
+ super(name)
+ end body
+ end init
+
+ // 重写父类方法,演示多态
+ function: getName
+ returns: int
+ body:
+ return super.getName() + 1000
+ end body
+ end function
+end struct
+end module
diff --git a/playground/Demo/Demo33/Main.snow b/playground/Demo/Demo33/Main.snow
new file mode 100644
index 0000000000000000000000000000000000000000..d681d77ce40eae8658d704be3d027a92de7fb0c2
--- /dev/null
+++ b/playground/Demo/Demo33/Main.snow
@@ -0,0 +1,14 @@
+module: main
+ import: os,Student, Person
+
+ function: main
+ returns: void
+ body:
+ declare a: Person = new Student(1)
+
+ os.println(a.getName())
+
+
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo33/OS.snow b/playground/Demo/Demo33/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo33/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo33/Person.snow b/playground/Demo/Demo33/Person.snow
new file mode 100644
index 0000000000000000000000000000000000000000..395cd06a266e37be8bacef005baf936bc2932d42
--- /dev/null
+++ b/playground/Demo/Demo33/Person.snow
@@ -0,0 +1,20 @@
+module: Person
+struct:Person
+ fields:
+ declare name: int
+ init:
+ params:
+ declare name: int
+ body:
+ this.name = name
+ end body
+ end init
+
+ function: getName
+ returns: int
+ body:
+ return this.name
+ end body
+ end function
+end struct
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo33/Student.snow b/playground/Demo/Demo33/Student.snow
new file mode 100644
index 0000000000000000000000000000000000000000..a1cf41a44c03605d8528a3ca603d44f542db5778
--- /dev/null
+++ b/playground/Demo/Demo33/Student.snow
@@ -0,0 +1,24 @@
+module: Student
+import : Person
+
+struct : Student extends Person
+ fields:
+ declare id: int
+
+ init:
+ params:
+ declare name: int
+ body:
+ super(name)
+ end body
+ end init
+
+ // 重写父类方法,演示多态
+ function: getName
+ returns: int
+ body:
+ return super.getName() + 1000
+ end body
+ end function
+end struct
+end module
diff --git a/playground/Demo/Demo4/Main.snow b/playground/Demo/Demo4/Main.snow
index aa376bc96a7c46cb482c631c4d12220b377c0a0d..f9e432f52e6519544841bb5c01d0b00892ee6b6f 100644
--- a/playground/Demo/Demo4/Main.snow
+++ b/playground/Demo/Demo4/Main.snow
@@ -1,11 +1,16 @@
module: Main
+ import:os
function: main
params:
returns: boolean
body:
- declare b1: boolean =true
+ declare b1: boolean =false
-
+ if (b1) then
+ os.print(0)
+ else
+ os.print(1)
+ end if
return b1
end body
end function
diff --git a/playground/Demo/Demo4/OS.snow b/playground/Demo/Demo4/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo4/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo5/Main.snow b/playground/Demo/Demo5/Main.snow
index 5a5c2b8cd2f31836ce56c5c5de832a1cc2f7baa9..f667f898d945a895a20b5af54584177a2d6ec5e2 100644
--- a/playground/Demo/Demo5/Main.snow
+++ b/playground/Demo/Demo5/Main.snow
@@ -1,4 +1,5 @@
module: Main
+ import:os
function: main
params:
returns: int
@@ -12,7 +13,7 @@ module: Main
step:
i = i + 1
body:
-
+ os.println(i)
end body
end loop
return 0
diff --git a/playground/Demo/Demo5/OS.snow b/playground/Demo/Demo5/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo5/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo6/Main.snow b/playground/Demo/Demo6/Main.snow
index 0f3b2dc23baef9b147dbc8e036c984d1e3e5140e..b4db5e4b2030938c787ba47b2c27b4d588608c0e 100644
--- a/playground/Demo/Demo6/Main.snow
+++ b/playground/Demo/Demo6/Main.snow
@@ -1,10 +1,11 @@
module: Main
+ import:os
function: main
params:
- returns: int
+ returns: void
body:
- declare b1 :int = -1
- return b1
+ declare b1 :int = -1+(-2)
+ os.print(b1)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo6/OS.snow b/playground/Demo/Demo6/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo6/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo7/Main.snow b/playground/Demo/Demo7/Main.snow
index f7e551cdafb4395dd2c698d2a9e9e3eb2d73ce52..77215ad407914a7a3d8db4a094ea40f2f3553312 100644
--- a/playground/Demo/Demo7/Main.snow
+++ b/playground/Demo/Demo7/Main.snow
@@ -1,10 +1,15 @@
module: Main
+ import:os
function: main
params:
- returns: boolean
+ returns: void
body:
declare b1 :boolean = true
- return !b1
+ if(!b1) then
+ os.println(0)
+ else
+ os.println(1)
+ end if
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo7/OS.snow b/playground/Demo/Demo7/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo7/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo8/OS.snow b/playground/Demo/Demo8/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo8/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/playground/Demo/Demo9/Main.snow b/playground/Demo/Demo9/Main.snow
index 61d51a80ffdcf3fd9e7fc1d03662f4a19992d97e..a7004fce9e84dbadf59a2a7f47fa0a27e2811eb8 100644
--- a/playground/Demo/Demo9/Main.snow
+++ b/playground/Demo/Demo9/Main.snow
@@ -1,10 +1,11 @@
module: Math
+ import:os
function: main
params:
- returns: int
+ returns: void
body:
- Math.factorial(6)
- return 0
+ declare n:int =Math.factorial(6)
+ os.print(n)
end body
end function
diff --git a/playground/Demo/Demo9/OS.snow b/playground/Demo/Demo9/OS.snow
new file mode 100644
index 0000000000000000000000000000000000000000..69dbb1ef755310a8a5e754b734056c25bd8d991c
--- /dev/null
+++ b/playground/Demo/Demo9/OS.snow
@@ -0,0 +1,18 @@
+module: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ function: println
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINTLN", i1)
+ end body
+ end function
+end module
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMProgramBuilder.java b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMProgramBuilder.java
index fab891c8d8c87cd3f9840c8e879889c184059285..f86dcb143bc4fb60c546737f31993bcfce5fed26 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMProgramBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMProgramBuilder.java
@@ -1,56 +1,85 @@
package org.jcnc.snow.compiler.backend.builder;
+import org.jcnc.snow.compiler.ir.builder.IRBuilderScope;
import org.jcnc.snow.vm.engine.VMOpCode;
+
import java.util.*;
/**
- * VMProgramBuilder 用于构建虚拟机(VM)的最终指令列表。
- *
- * 主要职责:
+ * {@code VMProgramBuilder} 负责后端阶段的 VM 指令序列组装及符号修补。
+ *
+ * 管理函数与标签到指令地址的映射
+ * 支持 CALL 和分支指令的延迟回填(符号修补)
+ * 支持槽位类型标注(用于类型检查和后端优化,可选)
+ *
+ *
+ * 符号修补机制:
*
- * 维护代码指令序列和符号地址表
- * 支持跨函数、标签跳转的延后修补(Call/Branch Fixup)
- * 支持虚拟机本地槽位类型的管理(如 I/F...)
+ * 支持根据函数简名进行匹配,例如 Student.getName 可匹配到 Person.getName,实现方法复用和继承支持。
+ * 自动处理 super 调用的符号绑定,例如 Student.super 可自动绑定到 Person.__init__。
+ * 所有未解决的 CALL 或分支符号在 build() 阶段将抛出异常,便于调试和定位错误。
*
*/
public final class VMProgramBuilder {
/**
- * 未知目标的 CALL 指令修补记录(待目标地址确定后修正)。
- * @param index CALL 指令在 code 列表中的位置
- * @param target 目标函数的全名
- * @param nArgs 参数个数
+ * 未解析地址的占位符,便于后期批量修补
*/
- private record CallFix(int index, String target, int nArgs) {}
-
+ private static final String PLACEHOLDER = "-1";
/**
- * 未知目标的分支指令修补记录(待目标标签确定后修正)。
- * @param index 分支指令在 code 列表中的位置
- * @param label 跳转目标标签名
+ * VM 指令列表
*/
- private record BranchFix(int index, String label) {}
-
- /** 未解析地址的占位符,便于后期批量修补 */
- private static final String PLACEHOLDER = "-1";
-
- /** VM 指令列表 */
private final List code = new ArrayList<>();
- /** 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化) */
+ /**
+ * 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化)
+ */
private final Map slotType = new HashMap<>();
- /** 符号(函数名/标签)到指令序号的映射表 */
+ /**
+ * 符号(函数名/标签)到指令序号的映射表
+ */
private final Map addr = new HashMap<>();
- /** 所有待修补的 CALL 指令集合 */
+ /**
+ * 所有待修补的 CALL 指令集合
+ */
private final List callFixes = new ArrayList<>();
- /** 所有待修补的分支指令集合 */
+ /**
+ * 所有待修补的分支指令集合
+ */
private final List branchFixes = new ArrayList<>();
- /** 当前代码指针(已生成指令的数量/下一个指令的位置) */
+ /**
+ * 当前代码指针(已生成指令的数量/下一个指令的位置)
+ */
private int pc = 0;
/**
- * 设置槽位(局部变量/虚拟寄存器)的类型前缀。
+ * 提取给定名称的最后一个片段。
+ * 主要用于从“模块.类.方法”这样的全限定名中,
+ * 提取出末尾的简单标识符。
*
- * @param slot 槽位编号
- * @param prefix 类型前缀(如 'I', 'F')
+ * @param name 输入的符号全名(可能包含多个 '.' 分隔的层级)
+ * @return 最后一个 '.' 之后的子串;如果没有 '.',则返回原始字符串
+ */
+ private static String lastSegment(String name) {
+ int i = name.lastIndexOf('.');
+ return (i < 0) ? name : name.substring(i + 1);
+ }
+
+ /**
+ * 读取当前已生成的代码列表(不可变视图)。
+ */
+ public List getCode() {
+ return Collections.unmodifiableList(code);
+ }
+
+ /**
+ * 获取当前 pc。
+ */
+ public int getPc() {
+ return pc;
+ }
+
+ /**
+ * 设置槽位的类型前缀(如 'I','F','R')。
*/
public void setSlotType(int slot, char prefix) {
slotType.put(slot, prefix);
@@ -58,39 +87,37 @@ public final class VMProgramBuilder {
/**
* 获取槽位的类型前缀,默认为 'I'(整数类型)。
- *
- * @param slot 槽位编号
- * @return 类型前缀字符
*/
public char getSlotType(int slot) {
return slotType.getOrDefault(slot, 'I');
}
/**
- * 标记一个函数或标签的起始位置。
- *
- * 1. 记录符号到当前 pc 的映射;
- * 2. 立即尝试修补之前所有针对该符号的延后调用和分支。
+ * 声明一个函数/标签的起始地址,并尝试修补所有引用到此符号的 CALL/BRANCH。
*
- * @param name 函数名或标签名(全限定名)
+ * @param name 函数或标签全名(如 "Person.getName"、"loop.start")
*/
public void beginFunction(String name) {
+ // 函数粒度隔离槽位类型,避免跨函数类型污染
+ slotType.clear();
addr.put(name, pc);
patchCallFixes(name);
patchBranchFixes(name);
}
- /** 函数结尾的处理(占位,无需特殊处理)。 */
- public void endFunction() {}
+ /**
+ * 函数结束接口,目前无具体实现,便于将来扩展。
+ */
+ public void endFunction() {
+ }
/**
- * 添加一条指令或标签到代码列表。
+ * 添加一条 VM 指令或标签(末尾':'视为标签)。
*
- * @param line 指令字符串或标签字符串(若以冒号结尾为标签)
+ * @param line 指令或标签
*/
public void emit(String line) {
if (line.endsWith(":")) {
- // 标签定义
String label = line.substring(0, line.length() - 1);
addr.put(label, pc);
patchBranchFixes(label);
@@ -101,21 +128,47 @@ public final class VMProgramBuilder {
}
/**
- * 添加一条 CALL 指令,若目标未定义则延后修补。
+ * 生成一条 {@code CALL} 指令。
+ *
+ * 该方法根据目标字符串 {@code target} 的特征,决定生成的 CALL 指令类型:
+ *
+ *
+ * 静态可解析调用:
+ * 如果 {@code target} 能够在编译/汇编期解析到绝对地址,
+ * 则生成形如 {@code CALL } 的指令。
*
- * @param target 目标函数全名
- * @param nArgs 参数个数
+ * 虚函数调用:
+ * 如果 {@code target} 包含 {@code "::"},但无法在静态期解析,
+ * 则认为是虚函数调用。此时生成形如
+ * {@code CALL @Class::method } 的指令,
+ * 并在运行时通过 vtable 进行方法查找。
+ *
+ * 待回填调用:
+ * 如果以上两种情况都不满足,则生成一个带占位符的指令
+ * {@code CALL },
+ * 并将该调用信息记录到 {@code callFixes} 列表中,
+ * 以便在后续回填阶段修正目标地址。
+ *
+ *
+ * @param target 调用目标,可以是绝对地址、类方法签名(如 {@code "Animal::speak"})或符号引用。
+ * @param nArgs 调用参数个数。
*/
public void emitCall(String target, int nArgs) {
- Integer a = resolve(target);
- if (a != null) {
- emit(VMOpCode.CALL + " " + a + " " + nArgs);
+ Integer addr = resolve(target);
+ if (addr != null) {
+ /* 静态可解析:直接生成绝对地址调用 */
+ emit(VMOpCode.CALL + " " + addr + " " + nArgs);
+ } else if (target.contains("::")) {
+ /* 虚函数调用:运行时通过 vtable 查找 */
+ emit(VMOpCode.CALL + " @" + target + " " + nArgs);
} else {
+ /* 待回填调用:记录占位符,稍后修正 */
emit(VMOpCode.CALL + " " + PLACEHOLDER + " " + nArgs);
callFixes.add(new CallFix(pc - 1, target, nArgs));
}
}
+
/**
* 添加一条分支指令(如 JMP/BR/BEQ),若目标未定义则延后修补。
*
@@ -125,9 +178,9 @@ public final class VMProgramBuilder {
public void emitBranch(String opcode, String label) {
Integer a = resolve(label);
if (a != null) {
- emit(opcode + ' ' + a);
+ emit(opcode + " " + a);
} else {
- emit(opcode + ' ' + PLACEHOLDER);
+ emit(opcode + " " + PLACEHOLDER);
branchFixes.add(new BranchFix(pc - 1, label));
}
}
@@ -135,16 +188,27 @@ public final class VMProgramBuilder {
/**
* 完成代码生成,输出最终 VM 指令序列。
*
- * 如果存在未修补的调用或分支,将抛出异常。
+ * 在最终报错前,统一做一次“继承链回填”:
+ *
+ * 优先尝试精确目标名(子类方法/重写优先)
+ * 否则递归查找父类同名方法
+ * 最后仅在唯一情况下允许“简名唯一匹配”
+ *
+ * 如果还有未修补的调用或分支,将抛出异常(包含全部未解析符号,便于调试)。
*
* @return 指令序列(不可变)
* @throws IllegalStateException 如果存在未修补符号
*/
public List build() {
+ // --- 统一做一次继承链回填 ---
+ patchRemainingFixesByInheritance();
+
if (!callFixes.isEmpty() || !branchFixes.isEmpty()) {
- throw new IllegalStateException(
- "Unresolved symbols — CALL: " + callFixes +
- ", BRANCH: " + branchFixes);
+ throw new IllegalStateException("""
+ Unresolved symbols while building:
+ calls = %s
+ branches= %s
+ """.formatted(callFixes, branchFixes));
}
return List.copyOf(code);
}
@@ -160,17 +224,119 @@ public final class VMProgramBuilder {
}
/**
- * 修补所有等待目标函数 name 的 CALL 指令。
- *
- * 只支持全名精确修补,不做模糊查找或短名回退。
+ * 在所有函数都已落址后,统一对剩余 CALL 进行继承链/最终回填:
+ * 1. 若存在“精确目标名”,优先绑定到它(保证子类重写优先);
+ * 2. 否则沿继承链向上查找第一个已定义的同名方法并绑定;
+ * 3. 若仍未命中,再尝试“简名唯一匹配”(避免多义性,只有唯一时才绑定);
+ * 未能解析者保留给后续的未解析报错逻辑。
+ */
+ private void patchRemainingFixesByInheritance() {
+ for (Iterator it = callFixes.iterator(); it.hasNext(); ) {
+ CallFix f = it.next();
+
+ // 1) 精确目标名(如 Student.getName),保证优先匹配到子类重写方法
+ Integer exact = addr.get(f.target);
+ if (exact != null) {
+ code.set(f.index, VMOpCode.CALL + " " + exact + " " + f.nArgs);
+ it.remove();
+ continue;
+ }
+
+ // 2) 沿父类链向上查找第一个已定义的同名方法
+ int dot = f.target.indexOf('.');
+ if (dot > 0) {
+ String curStruct = f.target.substring(0, dot);
+ String member = f.target.substring(dot + 1);
+
+ while (curStruct != null) {
+ String cand = curStruct + "." + member;
+ Integer a = addr.get(cand);
+ if (a != null) {
+ code.set(f.index, VMOpCode.CALL + " " + a + " " + f.nArgs);
+ it.remove();
+ break;
+ }
+ curStruct = IRBuilderScope.getStructParent(curStruct);
+ }
+ if (!it.hasNext()) { // 防止迭代器状态错误
+ // no-op
+ }
+ if (!callFixes.contains(f)) { // 已移除则继续
+ continue;
+ }
+ }
+
+ // 3) 简名唯一匹配(只允许唯一目标,否则放弃防止二义性)
+ if (dot < 0) {
+ String simple = f.target;
+ String chosen = null;
+ for (String k : addr.keySet()) {
+ int i = k.lastIndexOf('.');
+ String ks = (i >= 0) ? k.substring(i + 1) : k;
+ if (ks.equals(simple)) {
+ if (chosen != null && !chosen.equals(k)) {
+ chosen = null; // 多义性,放弃
+ break;
+ }
+ chosen = k;
+ }
+ }
+ if (chosen != null) {
+ int a = addr.get(chosen);
+ code.set(f.index, VMOpCode.CALL + " " + a + " " + f.nArgs);
+ it.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * 逐个函数定义出现时的回填:
+ * 仅处理‘全名精确匹配’、‘super(...) 构造调用’和‘原本未限定名的简名匹配’。
+ * 继承链回填改为在全部定义完成后统一进行,避免过早把子类调用绑到父类。
*
- * @param name 目标函数全名
+ * @param name 当前刚声明/定义的函数名(通常为全限定名)
*/
private void patchCallFixes(String name) {
- for (Iterator it = callFixes.iterator(); it.hasNext();) {
+ // 当前刚定义/落址的函数的“简名”(不含结构体前缀)
+ String nameSimple;
+ int cut = name.lastIndexOf('.');
+ nameSimple = (cut >= 0) ? name.substring(cut + 1) : name;
+
+ for (Iterator it = callFixes.iterator(); it.hasNext(); ) {
CallFix f = it.next();
- if (f.target.equals(name)) {
- code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs);
+
+ // 1) 全限定名精确匹配
+ boolean qualifiedMatch = f.target.equals(name);
+
+ // 2) super(...) 绑定(用于 __init__N)
+ boolean superMatch = false;
+ if (f.target.endsWith(".super") && name.contains(".__init__")) {
+ String tStruct = f.target.substring(0, f.target.length() - 6); // 去掉 ".super"
+ String nStruct = name.substring(0, name.indexOf(".__init__"));
+
+ int initArgc = -1;
+ try {
+ String num = name.substring(name.lastIndexOf("__init__") + 8);
+ initArgc = Integer.parseInt(num);
+ } catch (NumberFormatException ignored) {
+ }
+
+ // 结构名一致或参数个数一致即可认为匹配
+ if (tStruct.equals(nStruct) || initArgc == f.nArgs) {
+ superMatch = true;
+ }
+ }
+
+ // 3) 简名匹配(仅当“原始目标本来就是未限定名”时才允许)
+ String targetSimple;
+ int p = f.target.lastIndexOf('.');
+ targetSimple = (p >= 0) ? f.target.substring(p + 1) : f.target;
+ boolean simpleMatch = (p < 0) && targetSimple.equals(nameSimple);
+
+ if (qualifiedMatch || superMatch || simpleMatch) {
+ int targetAddr = addr.get(name);
+ code.set(f.index, VMOpCode.CALL + " " + targetAddr + " " + f.nArgs);
it.remove();
}
}
@@ -182,7 +348,7 @@ public final class VMProgramBuilder {
* @param label 目标标签
*/
private void patchBranchFixes(String label) {
- for (Iterator it = branchFixes.iterator(); it.hasNext();) {
+ for (Iterator it = branchFixes.iterator(); it.hasNext(); ) {
BranchFix f = it.next();
if (f.label.equals(label)) {
String patched = code.get(f.index).replace(PLACEHOLDER, addr.get(label).toString());
@@ -191,4 +357,16 @@ public final class VMProgramBuilder {
}
}
}
+
+ /**
+ * 未知目标的 CALL 指令修补记录(待目标地址确定后修正)。
+ */
+ private record CallFix(int index, String target, int nArgs) {
+ }
+
+ /**
+ * 未知目标的分支指令修补记录(待目标标签确定后修正)。
+ */
+ private record BranchFix(int index, String label) {
+ }
}
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
index 04dfaa110432df48831df1e79e60219a43c839d1..fbeb2d9af97a3bcde33e72ec485215e2634d3895 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java
@@ -16,40 +16,62 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
- * {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 层函数调用指令。
+ * CallGenerator 负责将 IR 层的 CallInstruction 转换为 VM 层的函数调用指令。
*
- * 支持 syscall、普通函数调用、一维/多维数组下标访问、以及字符串常量池绑定等功能。
- *
- *
+ * 功能包括:
*
- * syscall: 支持字符串常量与寄存器到子命令的绑定与解析
- * 数组下标访问: 支持所有主流基础类型 “__index_b/s/i/l/f/d/r”
- * 普通函数: 支持自动推断返回值类型与槽位类型
+ * 处理系统调用(syscall),支持字符串常量池
+ * 数组元素访问及赋值(__index / __setindex 系列)
+ * 普通函数调用,根据返回类型选择合适的 STORE 指令
*
*/
public class CallGenerator implements InstructionGenerator {
/**
- * 字符串常量池:用于绑定虚拟寄存器 id 到字符串值(供 syscall 子命令使用)。
- *
- * 使用 ConcurrentHashMap 保证并发安全,所有 registerStringConst 的注册和读取都是线程安全的。
+ * 字符串常量池,用于 syscall 子命令的字符串存储,
+ * 键为虚拟寄存器 ID,值为字符串常量。
*/
private static final Map STRING_CONST_POOL = new ConcurrentHashMap<>();
/**
- * 注册一个字符串常量,绑定到虚拟寄存器 id。
+ * 当前正在生成的函数名,用于错误上下文。
+ */
+ private String fn;
+
+ /**
+ * 注册字符串常量到常量池。
*
- * @param regId 虚拟寄存器 id
- * @param value 字符串常量
+ * @param regId 虚拟寄存器 ID
+ * @param value 字符串常量值
*/
public static void registerStringConst(int regId, String value) {
STRING_CONST_POOL.put(regId, value);
}
/**
- * 返回本生成器支持的 IR 指令类型。
+ * 将语言层类型名映射为 VM 指令的类型前缀字符。
*
- * @return {@link CallInstruction} 类型的 Class 对象
+ * @param name 类型名称,如 "int", "double", "string"
+ * @return 对应的 VM 类型前缀('I','D','R' 等)
+ */
+ private static char normalizeTypePrefix(String name) {
+ if (name == null) return 'I';
+ String n = name.toLowerCase(Locale.ROOT);
+ return switch (n) {
+ case "byte" -> 'B';
+ case "short" -> 'S';
+ case "int", "integer", "bool", "boolean" -> 'I';
+ case "long" -> 'L';
+ case "float" -> 'F';
+ case "double" -> 'D';
+ case "string" -> 'R'; // 字符串为引用类型
+ case "void" -> 'V';
+ default -> 'R'; // 结构体、自定义对象均视为引用类型
+ };
+ }
+
+ /**
+ * 返回本生成器支持的 IR 指令类型,此处为 CallInstruction。
*/
@Override
public Class supportedClass() {
@@ -57,12 +79,12 @@ public class CallGenerator implements InstructionGenerator {
}
/**
- * 生成指定的调用指令,包括 syscall、数组下标、普通函数。
+ * 核心生成方法,根据函数名分发到 syscall、数组操作或普通调用的处理逻辑。
*
- * @param ins IR 层的调用指令
- * @param out VM 程序构建器
- * @param slotMap 寄存器到槽位的映射表
- * @param currentFn 当前函数名
+ * @param ins 待转换的 IR 调用指令
+ * @param out VM 程序构建器,用于输出指令
+ * @param slotMap IR 虚拟寄存器到 VM 本地槽位的映射
+ * @param currentFn 当前所在的函数名,用于上下文传递
*/
@Override
public void generate(CallInstruction ins,
@@ -71,13 +93,13 @@ public class CallGenerator implements InstructionGenerator {
String currentFn) {
String fn = ins.getFunctionName();
- // 特殊处理 syscall 调用
+ // 处理特殊的 syscall 调用
if ("syscall".equals(fn) || fn.endsWith(".syscall")) {
generateSyscall(ins, out, slotMap, fn);
return;
}
- // 各种一维数组类型(byte/short/int/long/float/double/boolean)读取
+ // 处理数组读取和写入的内置函数
switch (fn) {
case "__index_b" -> {
generateIndexInstruction(ins, out, slotMap, 'B');
@@ -107,7 +129,6 @@ public class CallGenerator implements InstructionGenerator {
generateIndexInstruction(ins, out, slotMap, 'R');
return;
}
-
case "__setindex_b" -> {
generateSetIndexInstruction(ins, out, slotMap, 'B');
return;
@@ -138,24 +159,17 @@ public class CallGenerator implements InstructionGenerator {
}
}
- // 普通函数调用
+ // 默认:普通函数调用
generateNormalCall(ins, out, slotMap, fn);
}
- // ========== 私有辅助方法 ==========
-
/**
- * 生成数组元素赋值指令(arr[idx] = value),无返回值。
- *
- * 调用栈压栈顺序为:arr (引用类型, R),idx (整型, I),value (元素类型, T)。
- * 执行 SYSCALL ARR_SET 指令以完成数组赋值操作。
- *
+ * 生成数组元素赋值指令。
*
- * @param ins 当前的调用指令(包含参数信息)
- * @param out VM 指令生成器(用于输出 VM 指令)
- * @param slotMap 虚拟寄存器与槽位的映射表
- * @param valType 待写入的 value 的类型标识('B': byte, 'S': short, 'I': int, 'L': long, 'F': float, 'D': double, 其余为引用类型'R')
- * @throws IllegalStateException 如果参数数量不为3,则抛出异常
+ * @param ins 调用 __setindex_* 的 IR 指令
+ * @param out VM 指令构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param valType 元素值的类型前缀(如 'I','R' 等)
*/
private void generateSetIndexInstruction(CallInstruction ins,
VMProgramBuilder out,
@@ -163,264 +177,189 @@ public class CallGenerator implements InstructionGenerator {
char valType) {
List args = ins.getArguments();
if (args.size() != 3) {
- // 参数数量错误,抛出异常并输出实际参数列表
- throw new IllegalStateException(
- "[CallGenerator] __setindex_* 需要三个参数(arr, idx, value),实际: " + args);
+ throw new IllegalStateException("[CallGenerator] __setindex_* 需要三个参数 (arr, idx, value),实际: " + args);
}
-
- // 第一个参数为数组对象,压入引用类型寄存器('R'),如 arr
- loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName());
-
- // 第二个参数为索引值,压入整型寄存器('I'),如 idx
- loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName());
-
- // 第三个参数为待赋值元素,根据元素类型压入相应类型寄存器
- // 支持类型:'B'(byte), 'S'(short), 'I'(int), 'L'(long), 'F'(float), 'D'(double)
- // 其他情况(如引用类型),按'R'处理
- switch (valType) {
- case 'B' -> loadArgument(out, slotMap, args.get(2), 'B', ins.getFunctionName());
- case 'S' -> loadArgument(out, slotMap, args.get(2), 'S', ins.getFunctionName());
- case 'I' -> loadArgument(out, slotMap, args.get(2), 'I', ins.getFunctionName());
- case 'L' -> loadArgument(out, slotMap, args.get(2), 'L', ins.getFunctionName());
- case 'F' -> loadArgument(out, slotMap, args.get(2), 'F', ins.getFunctionName());
- case 'D' -> loadArgument(out, slotMap, args.get(2), 'D', ins.getFunctionName());
- default -> loadArgument(out, slotMap, args.get(2), 'R', ins.getFunctionName());
- }
-
- // 输出 VM 指令:SYSCALL ARR_SET,完成数组元素写入操作
- out.emit(VMOpCode.SYSCALL + " " + "ARR_SET");
+ loadArgument(out, slotMap, args.get(0), 'R', ins.getFunctionName()); // 数组引用
+ loadArgument(out, slotMap, args.get(1), 'I', ins.getFunctionName()); // 索引
+ loadArgument(out, slotMap, args.get(2), valType, ins.getFunctionName()); // 值
+ out.emit(VMOpCode.SYSCALL + " ARR_SET");
}
-
/**
- * 解析 syscall 子命令(第一个参数),支持字符串常量与已绑定字符串的虚拟寄存器。
+ * 生成数组元素读取指令。
*
- * @param arg 子命令参数(应为字符串常量或寄存器)
- * @param fn 当前函数名(仅用于报错信息)
- * @return 子命令(大写字符串)
- * @throws IllegalStateException 如果参数不是字符串常量或已绑定寄存器
- */
- private String resolveSyscallSubcmd(IRValue arg, String fn) {
- switch (arg) {
- case IRConstant(String s) -> {
- return s.toUpperCase(Locale.ROOT);
- }
- case IRConstant(Object value) -> throw new IllegalStateException(
- "[CallGenerator] syscall 第一个参数必须是字符串常量 (function: %s, value: %s)"
- .formatted(fn, value)
- );
- case IRVirtualRegister(int id) -> {
- String s = STRING_CONST_POOL.get(id);
- if (s == null) {
- throw new IllegalStateException(
- "[CallGenerator] 未找到 syscall 字符串常量绑定 (function: %s, regId: %d)"
- .formatted(fn, id)
- );
- }
- return s.toUpperCase(Locale.ROOT);
- }
- case null, default -> throw new IllegalStateException(
- "[CallGenerator] syscall 第一个参数必须是字符串常量或已绑定字符串的寄存器 (function: %s, arg: %s)"
- .formatted(fn, arg)
- );
- }
- }
-
- /**
- * 加载一个参数到栈,支持指定默认类型。如果未设置类型则采用默认类型。
- *
- * @param out VM 程序构建器
- * @param slotMap 寄存器到槽位的映射
- * @param arg 参数值(应为虚拟寄存器)
- * @param defaultType 默认类型
- * @param fn 当前函数名(用于错误提示)
- * @throws IllegalStateException 如果参数不是虚拟寄存器,或槽位未找到
- */
- private void loadArgument(VMProgramBuilder out, Map slotMap, IRValue arg, char defaultType, String fn) {
- if (!(arg instanceof IRVirtualRegister vr)) {
- throw new IllegalStateException(
- "[CallGenerator] 参数必须为虚拟寄存器 (function: %s, arg: %s)".formatted(fn, arg));
- }
- Integer slot = slotMap.get(vr);
- if (slot == null) {
- throw new IllegalStateException(
- "[CallGenerator] 未找到虚拟寄存器的槽位映射 (function: %s, reg: %s)".formatted(fn, vr));
- }
- char t = out.getSlotType(slot);
- if (t == '\0') t = defaultType;
- out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
- }
-
- /**
- * 生成一维/多维数组的下标访问指令(支持类型分派)。
- *
- * 本方法用于将 IR 层的数组下标访问表达式(如 arr[idx]),生成对应的 VM 指令序列。
- * 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问。
- *
- *
- *
- * 1. 依次加载数组参数(arr)和下标参数(idx)到操作数栈
- * 2. 发出 ARR_GET 系统调用指令(SYSCALL ARR_GET),通过 VM 访问对应元素
- * 3. 根据元素声明类型,将结果写入目标槽位,支持类型分派(B/S/I/L/F/D/R)
- * 4. 若参数或返回寄存器缺失,则抛出异常提示
- *
- *
- * @param ins 下标访问对应的 IR 调用指令,函数名通常为 __index_x
- * @param out VM 程序构建器,用于发出 VM 指令
- * @param slotMap IR 虚拟寄存器到 VM 槽位的映射表
- * @param retType 元素类型标识('B' byte, 'S' short, 'I' int, 'L' long, 'F' float, 'D' double, 'R' ref/obj)
- * @throws IllegalStateException 参数个数不符、缺少目标寄存器、未找到槽位等情况
+ * @param ins 调用 __index_* 的 IR 指令
+ * @param out VM 指令构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param retType 返回值类型前缀
*/
- private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map slotMap, char retType) {
+ private void generateIndexInstruction(CallInstruction ins,
+ VMProgramBuilder out,
+ Map slotMap,
+ char retType) {
String fn = ins.getFunctionName();
List args = ins.getArguments();
if (args.size() != 2) {
- throw new IllegalStateException(
- "[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, args));
+ throw new IllegalStateException("[CallGenerator] " + fn + " 需要两个参数 (arr, idx),实际: " + args);
}
- // 加载数组参数(寄存器类型按 R/ref 处理,默认对象槽位)
- loadArgument(out, slotMap, args.get(0), 'R', fn);
- // 加载下标参数(寄存器类型按 I/int 处理)
- loadArgument(out, slotMap, args.get(1), 'I', fn);
-
- // 发出 ARR_GET 系统调用(元素访问由 VM 完成类型分派)
- out.emit(VMOpCode.SYSCALL + " " + "ARR_GET");
+ loadArgument(out, slotMap, args.get(0), 'R', fn); // 数组引用
+ loadArgument(out, slotMap, args.get(1), 'I', fn); // 索引
+ out.emit(VMOpCode.SYSCALL + " ARR_GET");
- // 保存返回值到目标寄存器
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
- throw new IllegalStateException(
- "[CallGenerator] %s 需要有目标寄存器用于保存返回值".formatted(fn));
+ throw new IllegalStateException("[CallGenerator] " + fn + " 必须有返回值寄存器");
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
- throw new IllegalStateException(
- "[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest));
+ throw new IllegalStateException("[CallGenerator] " + fn + " 未找到目标槽位");
}
-
- // 按元素类型分派写入 VM 槽位
+ // 根据类型选择 STORE 指令
switch (retType) {
- case 'B' -> {
- out.emit(OpHelper.opcode("B_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'B');
- }
- case 'S' -> {
- out.emit(OpHelper.opcode("S_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'S');
- }
- case 'I' -> {
- out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'I');
- }
- case 'L' -> {
- out.emit(OpHelper.opcode("L_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'L');
- }
- case 'F' -> {
- out.emit(OpHelper.opcode("F_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'F');
- }
- case 'D' -> {
- out.emit(OpHelper.opcode("D_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'D');
- }
- default -> {
- out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'R');
- }
+ case 'B' -> out.emit(OpHelper.opcode("B_STORE") + " " + destSlot);
+ case 'S' -> out.emit(OpHelper.opcode("S_STORE") + " " + destSlot);
+ case 'I' -> out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
+ case 'L' -> out.emit(OpHelper.opcode("L_STORE") + " " + destSlot);
+ case 'F' -> out.emit(OpHelper.opcode("F_STORE") + " " + destSlot);
+ case 'D' -> out.emit(OpHelper.opcode("D_STORE") + " " + destSlot);
+ default -> out.emit(OpHelper.opcode("R_STORE") + " " + destSlot);
}
+ out.setSlotType(destSlot, retType);
}
/**
- * 生成 syscall 指令分支逻辑。
- *
- * 解析 syscall 子命令
- * 压栈剩余参数
- * 发出 SYSCALL 指令
- * 若有返回值则保存至目标槽位
- *
+ * 生成系统调用(syscall)指令。
+ * 首个参数为子命令,支持常量或寄存器引用;其余参数均以引用形式加载。
+ * 若存在返回寄存器,则将结果存为整数类型。
*
- * @param ins 调用指令
- * @param out VM 程序构建器
- * @param slotMap 寄存器到槽位映射
- * @param fn 当前函数名
+ * @param ins syscall 对应的 IR 调用指令
+ * @param out VM 指令构建器
+ * @param slotMap 寄存器到槽位的映射
+ * @param fn 函数名("syscall" 或以 ".syscall" 结尾)
*/
- private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map slotMap, String fn) {
+ private void generateSyscall(CallInstruction ins,
+ VMProgramBuilder out,
+ Map slotMap,
+ String fn) {
List args = ins.getArguments();
if (args.isEmpty()) {
- throw new IllegalStateException(
- "[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
+ throw new IllegalStateException("[CallGenerator] syscall 至少需要一个子命令");
}
-
- // 0. 解析 syscall 子命令(第一个参数)
- String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
-
- // 1. 压栈其余 syscall 参数(从 index 1 开始)
+ String subcmd = resolveSyscallSubcmd(args.get(0), fn);
for (int i = 1; i < args.size(); i++) {
loadArgument(out, slotMap, args.get(i), 'R', fn);
}
-
- // 2. 生成 SYSCALL 指令
out.emit(VMOpCode.SYSCALL + " " + subcmd);
- // 3. 有返回值则保存到目标槽位
IRVirtualRegister dest = ins.getDest();
if (dest != null) {
- Integer destSlot = slotMap.get(dest);
- if (destSlot == null) {
- throw new IllegalStateException(
- "[CallGenerator] syscall 未找到目标寄存器的槽位映射 (function: %s, dest: %s)"
- .formatted(fn, dest));
+ Integer slot = slotMap.get(dest);
+ if (slot == null) throw new IllegalStateException("[CallGenerator] syscall 未找到目标槽位");
+ out.emit(OpHelper.opcode("I_STORE") + " " + slot);
+ out.setSlotType(slot, 'I');
+ }
+ }
+
+ /**
+ * 解析 syscall 子命令字符串:
+ * 若为 IRConstant,则直接取字符串值;
+ * 若为 IRVirtualRegister,则从常量池中获取。
+ * 最终返回大写子命令名称。
+ *
+ * @param arg 第一个参数,常量或虚拟寄存器
+ * @param fn 所属函数名,用于错误提示
+ * @return 大写的子命令字符串
+ * @throws IllegalStateException 参数类型或常量池缺失时抛出
+ */
+ private String resolveSyscallSubcmd(IRValue arg, String fn) {
+ if (arg instanceof IRConstant(Object value)) {
+ if (value instanceof String s) {
+ return s.toUpperCase(Locale.ROOT);
}
- out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
- out.setSlotType(destSlot, 'I');
+ throw new IllegalStateException("[CallGenerator] syscall 子命令必须是字符串 (函数: " + fn + ")");
+ }
+ if (arg instanceof IRVirtualRegister(int id)) {
+ String s = STRING_CONST_POOL.get(id);
+ if (s == null) throw new IllegalStateException("[CallGenerator] 未注册的 syscall 字符串常量");
+ return s.toUpperCase(Locale.ROOT);
}
+ throw new IllegalStateException("[CallGenerator] syscall 子命令参数错误: " + arg);
}
/**
* 生成普通函数调用指令:
- *
- * 推断返回值类型
- * 压栈所有参数
- * 生成 CALL 指令
- * 保存返回值(若非 void)
- *
+ * 参数均以引用形式压栈,调用完成后,若返回类型非 void,
+ * 则选择合适的 STORE 指令保存结果。
*
* @param ins 调用指令
- * @param out VM 程序构建器
+ * @param out 指令构建器
* @param slotMap 寄存器到槽位映射
- * @param fn 当前函数名
+ * @param fn 被调用函数名称
*/
- private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map slotMap, String fn) {
- // 1. 推断返回值类型(首字母大写,缺省为 'I')
+ private void generateNormalCall(CallInstruction ins,
+ VMProgramBuilder out,
+ Map slotMap,
+ String fn) {
String retTypeName = GlobalFunctionTable.getReturnType(fn);
- char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
+ char retType = normalizeTypePrefix(retTypeName);
- // 2. 压栈所有参数
for (IRValue arg : ins.getArguments()) {
loadArgument(out, slotMap, arg, 'R', fn);
}
-
- // 3. 发出 CALL 指令
out.emitCall(fn, ins.getArguments().size());
- // 3.5 void 返回直接结束
if ("void".equals(retTypeName)) {
return;
}
-
- // 4. 保存返回值到目标寄存器
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
- throw new IllegalStateException(
- "[CallGenerator] 普通函数调用未找到目标寄存器 (function: %s)".formatted(fn));
+ throw new IllegalStateException("[CallGenerator] 普通调用缺少返回寄存器");
}
- Integer destSlot = slotMap.get(dest);
- if (destSlot == null) {
- throw new IllegalStateException(
- "[CallGenerator] 普通函数调用未找到目标寄存器的槽位映射 (function: %s, dest: %s)".formatted(fn, dest));
+ Integer slot = slotMap.get(dest);
+ if (slot == null) {
+ throw new IllegalStateException("[CallGenerator] 未找到返回槽位");
}
- out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
- out.setSlotType(destSlot, retType);
+ out.emit(OpHelper.opcode(retType + "_STORE") + " " + slot);
+ out.setSlotType(slot, retType);
+ }
+
+ /**
+ * 加载参数:
+ * 验证为虚拟寄存器后,获取槽位和类型,
+ * 缺省时使用 defaultType,然后发出 LOAD 指令。
+ *
+ * @param out 指令构建器
+ * @param slotMap 寄存器到槽位映射
+ * @param arg IR 参数值
+ * @param defaultType 缺省类型前缀
+ * @param fn 所属函数名,用于错误提示
+ */
+ private void loadArgument(VMProgramBuilder out,
+ Map slotMap,
+ IRValue arg,
+ char defaultType,
+ String fn) {
+ this.fn = fn;
+ if (!(arg instanceof IRVirtualRegister vr)) {
+ throw new IllegalStateException("[CallGenerator] 参数必须是 IRVirtualRegister");
+ }
+ Integer slot = slotMap.get(vr);
+ if (slot == null) {
+ throw new IllegalStateException("[CallGenerator] 未找到参数槽位");
+ }
+ char t = out.getSlotType(slot);
+ if (t == '\0' || (t == 'I' && defaultType != 'I')) {
+ t = defaultType;
+ }
+ out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
+ }
+
+ /**
+ * 返回最近一次处理的函数名,可用于调试。
+ *
+ * @return 函数名字符串
+ */
+ public String getFn() {
+ return fn;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
index 05aefa5b8709c9907371465e350a70e88c5fc23c..0e1b573e5f01a0107d30b480e50f6f9c4d089767 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
@@ -43,12 +43,7 @@ public record ExpressionBuilder(IRContext ctx) {
// 布尔字面量,例如 true / false
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
// 标识符(变量名),如 a、b
- case IdentifierNode id -> {
- // 查找当前作用域中的变量寄存器
- IRVirtualRegister reg = ctx.getScope().lookup(id.name());
- if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
- yield reg;
- }
+ case IdentifierNode id -> buildIdentifier(id);
// 模块常量 / 全局变量,如 ModuleA.a
case MemberExpressionNode mem -> buildMember(mem);
// 二元表达式(如 a+b, x==y)
@@ -59,6 +54,7 @@ public record ExpressionBuilder(IRContext ctx) {
case UnaryExpressionNode un -> buildUnary(un);
case IndexExpressionNode idx -> buildIndex(idx);
case ArrayLiteralNode arr -> buildArrayLiteral(arr);
+ case NewExpressionNode n -> buildNew(n);
// 默认分支:遇到未知表达式类型则直接抛异常
default -> throw new IllegalStateException(
"不支持的表达式类型: " + expr.getClass().getSimpleName());
@@ -66,76 +62,182 @@ public record ExpressionBuilder(IRContext ctx) {
}
/**
- * 成员访问表达式构建
+ * 构造标识符节点对应的 IR 虚拟寄存器。
+ *
+ * 支持普通变量查找,以及结构体方法/构造器中的“字段回退”机制(即自动将裸标识符回退为 this.)。
*
- * @param mem 成员表达式节点
- * @return 存储结果的虚拟寄存器
+ * @param id 标识符节点
+ * @return 查找到的 IR 虚拟寄存器
+ * @throws IllegalStateException 若标识符未定义且无法回退为字段
*/
- private IRVirtualRegister buildMember(MemberExpressionNode mem) {
- if (!(mem.object() instanceof IdentifierNode id)) {
- throw new IllegalStateException("不支持的成员访问对象类型: "
- + mem.object().getClass().getSimpleName());
+ private IRVirtualRegister buildIdentifier(IdentifierNode id) {
+ // ====================== 普通变量查找 ======================
+ // 1. 在当前作用域查找变量(可能是局部变量、形参、临时变量等)
+ IRVirtualRegister reg = ctx.getScope().lookup(id.name());
+ if (reg != null) return reg;
+
+ // ====================== 字段回退机制 ======================
+ // 2. 若未找到,则判断是否处于结构体方法或构造器中
+ // 尝试将裸标识符自动视为 this.,即访问当前结构体实例的成员字段
+ IRVirtualRegister thisReg = ctx.getScope().lookup("this");
+ String thisType = ctx.getScope().lookupType("this");
+ if (thisReg != null && thisType != null) {
+ // 生成成员表达式节点 this.
+ MemberExpressionNode asField = new MemberExpressionNode(
+ new IdentifierNode("this", id.context()), // 构造 this 节点
+ id.name(), // 字段名
+ id.context()
+ );
+ // 递归构造成员访问(相当于构造 this. 的 IR)
+ return buildMember(asField);
}
- String qualified = id.name() + "." + mem.member();
- /* 1) 尝试直接获取已有寄存器绑定 */
- IRVirtualRegister reg = ctx.getScope().lookup(qualified);
- if (reg != null) {
- return reg;
+ // ====================== 标识符未定义异常 ======================
+ // 3. 无法查找到变量且不能字段回退,抛出异常
+ throw new IllegalStateException("未定义标识符: " + id.name());
+ }
+
+
+ /**
+ * 构建成员访问表达式的 IR 虚拟寄存器。
+ *
+ * 支持两类成员访问:
+ *
+ * 模块常量访问(如 ModuleName.CONST)
+ * 结构体/对象字段访问(如 obj.field 或 this.field)
+ *
+ *
+ * @param mem 成员访问表达式节点(如 a.b 或 ModuleName.CONST)
+ * @return 存储成员值的 IR 虚拟寄存器
+ * @throws IllegalStateException 若找不到成员或无法解析类型
+ */
+ private IRVirtualRegister buildMember(MemberExpressionNode mem) {
+ // ===== 1. 处理模块常量 (ModuleName.member) =====
+ // 检查成员访问的对象是否为一个标识符(即模块名)
+ if (mem.object() instanceof IdentifierNode oid) {
+ String mod = oid.name();
+ // 查找是否存在该模块下的全局常量定义
+ Object c = GlobalConstTable.get(mod + "." + mem.member());
+ if (c != null) {
+ // 若找到常量,分配新寄存器并生成 LoadConst 指令
+ IRVirtualRegister r = ctx.newRegister();
+ ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(c)));
+ return r;
+ }
}
- /* 2) 折叠为编译期常量:先查作用域,再查全局常量表 */
- Object v = ctx.getScope().getConstValue(qualified);
- if (v == null) {
- v = GlobalConstTable.get(qualified);
+ // ===== 2. 结构体/对象字段访问 =====
+
+ // 1 递归构建成员对象(如 a.b,先获得 a 的寄存器)
+ IRVirtualRegister objReg = build(mem.object());
+
+ // 2 尝试解析成员访问接收者(object)的类型
+ String ownerType = null;
+ if (mem.object() instanceof IdentifierNode oid) {
+ // 如果对象是标识符,直接查询其类型(例如 a.x,a 的类型)
+ ownerType = ctx.getScope().lookupType(oid.name());
}
- if (v != null) {
- IRVirtualRegister r = ctx.newRegister();
- ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(v)));
- return r;
+ if (ownerType == null || ownerType.isEmpty()) {
+ // 兜底:如果访问 this.xxx,并且 this 有类型,则使用 this 的类型
+ String thisType = ctx.getScope().lookupType("this");
+ if (thisType != null) ownerType = thisType;
+ }
+ if (ownerType == null || ownerType.isEmpty()) {
+ // 类型无法解析,抛出异常
+ throw new IllegalStateException("无法解析成员访问接收者的类型");
}
- throw new IllegalStateException("未定义的常量: " + qualified);
- }
+ // 3 查找字段槽位下标:ownerType 的 mem.member() 字段的序号
+ Integer fieldIndex = ctx.getScope().lookupFieldIndex(ownerType, mem.member());
+ if (fieldIndex == null) {
+ // 字段不存在,抛出异常
+ throw new IllegalStateException("类型 " + ownerType + " 不存在字段: " + mem.member());
+ }
+ // 4 生成读取字段的 IR 指令:CALL __index_r, objReg, const(fieldIndex)
+ // 4.1 先将字段下标加载到寄存器
+ IRVirtualRegister idxReg = ctx.newRegister();
+ ctx.addInstruction(new LoadConstInstruction(
+ idxReg,
+ IRConstant.fromNumber(Integer.toString(fieldIndex))
+ ));
+
+ // 4.2 生成成员读取调用指令
+ IRVirtualRegister out = ctx.newRegister();
+ List args = new ArrayList<>();
+ args.add(objReg); // 对象寄存器
+ args.add(idxReg); // 字段下标寄存器
+ ctx.addInstruction(new CallInstruction(out, "__index_r", args));
+ return out;
+ }
- /* ───────────────── 写入指定寄存器 ───────────────── */
/**
- * 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}。
+ * 将表达式节点 {@link ExpressionNode} 的求值结果写入指定的目标虚拟寄存器 {@code dest}。
*
- * 按表达式类型分派处理,包括:
- *
- * 字面量(数字、字符串、布尔、数组):生成 loadConst 指令直接写入目标寄存器
- * 变量标识符:查表获取源寄存器,并 move 到目标寄存器
- * 二元表达式、下标、调用表达式:递归生成子表达式结果,并写入目标寄存器
- * 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
- *
+ * 根据不同表达式类型,采取高效或递归方式生成中间代码,涵盖常量、变量、运算、数组、调用等。
*
*
- * @param node 要求值的表达式节点
- * @param dest 结果目标虚拟寄存器
- * @throws IllegalStateException 若标识符未定义(如变量未声明时引用)
+ * @param node 表达式节点(可为字面量、变量、数组、运算等)
+ * @param dest 目标虚拟寄存器(写入结果)
+ * @throws IllegalStateException 若变量标识符未定义
*/
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) {
- // 数字字面量:生成 loadConst 指令,将数值常量写入目标寄存器
+ // ===================== 数字字面量 =====================
+ // 如 42、3.14 等,直接生成 loadConst 指令,常量写入目标寄存器
case NumberLiteralNode n -> InstructionFactory.loadConstInto(
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
- // 字符串字面量:生成 loadConst 指令,将字符串常量写入目标寄存器
+ // ===================== 字符串字面量 =====================
+ // 如 "hello",直接生成 loadConst 指令
case StringLiteralNode s -> InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(s.value()));
- // 布尔字面量:转换为 int 1/0,生成 loadConst 指令写入目标寄存器
+ // ===================== 布尔字面量 =====================
+ // 如 true/false,转换为 int 常量 1/0,生成 loadConst
case BoolLiteralNode b -> InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
- // 数组字面量:生成数组常量并写入目标寄存器
+ // ===================== 数组字面量 =====================
+ // 直接构造数组常量(只支持静态初始化),生成 loadConst
case ArrayLiteralNode arr -> InstructionFactory.loadConstInto(
ctx, dest, buildArrayConstant(arr));
- // 变量标识符:查表获得源寄存器,move 到目标寄存器
+ // ===================== new 表达式(构造数组/对象) =====================
+ // 生成空数组,然后按参数依次初始化每一项,使用 __setindex_r 填充目标寄存器
+ case NewExpressionNode newExpr -> {
+ // 1. 把空列表写入目标寄存器
+ InstructionFactory.loadConstInto(ctx, dest, new IRConstant(java.util.List.of()));
+
+ // 2. 依次写入构造实参,同时缓存参数寄存器
+ List argRegs = new ArrayList<>();
+ for (int i = 0; i < newExpr.arguments().size(); i++) {
+ IRVirtualRegister argReg = build(newExpr.arguments().get(i));
+ argRegs.add(argReg);
+
+ IRVirtualRegister idxReg = ctx.newTempRegister();
+ InstructionFactory.loadConstInto(ctx, idxReg, new IRConstant(i));
+
+ ctx.addInstruction(new CallInstruction(
+ null,
+ "__setindex_r",
+ List.of(dest, idxReg, argReg)));
+ }
+
+ /* 3. 若确认为结构体,显式调用 .__init__N 完成字段初始化 */
+ if (IRBuilderScope.getStructLayout(newExpr.typeName()) != null) {
+ String ctor = newExpr.typeName() + ".__init__" + argRegs.size();
+ List ctorArgs = new ArrayList<>();
+ ctorArgs.add(dest); // this
+ ctorArgs.addAll(argRegs); // 实参
+ ctx.addInstruction(new CallInstruction(null, ctor, ctorArgs));
+ }
+ }
+
+
+ // ===================== 变量标识符 =====================
+ // 如 x,查找符号表,move 到目标寄存器。未定义时报错。
case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null)
@@ -143,22 +245,26 @@ public record ExpressionBuilder(IRContext ctx) {
InstructionFactory.move(ctx, src, dest);
}
- // 二元表达式:递归生成左右子表达式,并将结果写入目标寄存器
+ // ===================== 二元表达式(如 a + b) =====================
+ // 递归生成左右操作数,并将运算结果写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
- // 下标表达式:递归生成索引结果,move 到目标寄存器
+ // ===================== 下标访问(如 arr[1]) =====================
+ // 先递归构造表达式,将索引结果 move 到目标寄存器
case IndexExpressionNode idx -> {
IRVirtualRegister tmp = buildIndex(idx);
InstructionFactory.move(ctx, tmp, dest);
}
- // 调用表达式:递归生成调用结果,move 到目标寄存器
+ // ===================== 函数/方法调用(如 foo(a, b)) =====================
+ // 递归生成调用,结果 move 到目标寄存器
case CallExpressionNode call -> {
IRVirtualRegister tmp = buildCall(call);
InstructionFactory.move(ctx, tmp, dest);
}
- // 其它类型:统一先 build 到临时寄存器,再 move 到目标寄存器
+ // ===================== 其它所有情况(兜底处理) =====================
+ // 通用流程:先生成结果到临时寄存器,再 move 到目标寄存器
default -> {
IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest);
@@ -315,46 +421,58 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 编译期常量值(支持 int、double、String、List),否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
- if (expr == null) return null;
-
- // 数字字面量:尝试解析为 int 或 double
- if (expr instanceof NumberLiteralNode n) {
- String s = n.value();
- try {
- if (s.contains(".") || s.contains("e") || s.contains("E"))
- return Double.parseDouble(s); // 带小数或科学计数法为 double
- return Integer.parseInt(s); // 否则为 int
- } catch (NumberFormatException e) {
- return null; // 无法解析为数字
+ switch (expr) {
+ case null -> {
+ return null;
}
- }
- // 字符串字面量:直接返回字符串
- if (expr instanceof StringLiteralNode s) return s.value();
+ // 数字字面量:尝试解析为 int 或 double
+ case NumberLiteralNode n -> {
+ String s = n.value();
+ try {
+ if (s.contains(".") || s.contains("e") || s.contains("E"))
+ return Double.parseDouble(s); // 带小数或科学计数法为 double
+ return Integer.parseInt(s); // 否则为 int
+ } catch (NumberFormatException e) {
+ return null; // 无法解析为数字
+ }
+ }
- // 布尔字面量:true 返回 1,false 返回 0
- if (expr instanceof BoolLiteralNode b) return b.getValue() ? 1 : 0;
- // 数组字面量:递归折叠所有元素
- if (expr instanceof ArrayLiteralNode arr) {
- List list = new ArrayList<>();
- for (ExpressionNode e : arr.elements()) {
- Object v = tryFoldConst(e);
- if (v == null) return null; // 有一项无法折叠则整体失败
- list.add(v);
+ // 字符串字面量:直接返回字符串
+ case StringLiteralNode s -> {
+ return s.value();
}
- return List.copyOf(list);
- }
- // 标识符:尝试查找作用域中的常量值
- if (expr instanceof IdentifierNode id) {
- Object v = null;
- try {
- v = ctx.getScope().getConstValue(id.name());
- } catch (Throwable ignored) {
- // 查不到常量或异常都视为无法折叠
+ // 布尔字面量:true 返回 1,false 返回 0
+ case BoolLiteralNode b -> {
+ return b.getValue() ? 1 : 0;
+ }
+
+ // 数组字面量:递归折叠所有元素
+ case ArrayLiteralNode arr -> {
+ List list = new ArrayList<>();
+ for (ExpressionNode e : arr.elements()) {
+ Object v = tryFoldConst(e);
+ if (v == null) return null; // 有一项无法折叠则整体失败
+ list.add(v);
+ }
+ return List.copyOf(list);
+ }
+
+
+ // 标识符:尝试查找作用域中的常量值
+ case IdentifierNode id -> {
+ Object v = null;
+ try {
+ v = ctx.getScope().getConstValue(id.name());
+ } catch (Throwable ignored) {
+ // 查不到常量或异常都视为无法折叠
+ }
+ return v;
+ }
+ default -> {
}
- return v;
}
// 其它类型:不支持折叠,返回 null
@@ -397,47 +515,187 @@ public record ExpressionBuilder(IRContext ctx) {
/**
- * 构建函数或方法调用表达式。
+ * 构造 new 对象/数组创建表达式的 IR 虚拟寄存器(支持列表型构造)。
*
- * 支持普通函数调用(foo(a, b))与成员方法调用(obj.method(a, b))。
+ * 语义说明:本方法用于生成“new 表达式”的中间代码流程,具体包括:
*
- * 首先递归生成所有参数的虚拟寄存器列表。
- * 根据 callee 类型区分成员访问或直接标识符调用,并规范化方法名(如加前缀)。
- * 为返回值分配新寄存器,生成 Call 指令。
+ * 分配一个新的寄存器用于保存新对象/数组引用
+ * 将一个空列表常量写入该寄存器(后端 runtime 识别为可变列表/对象)
+ * 遍历所有构造参数,依次写入目标列表的 [0..n-1] 位置
+ * 最终返回该寄存器
*
- *
*
- * @param call 函数/方法调用表达式节点
- * @return 存放调用结果的虚拟寄存器
+ * @param node new 表达式节点,包含所有构造参数
+ * @return 保存新创建对象/数组引用的目标寄存器
+ */
+ private IRVirtualRegister buildNew(NewExpressionNode node) {
+ // 1. 分配新的寄存器作为 new 表达式的结果
+ IRVirtualRegister dest = ctx.newRegister();
+
+ /* 1. 写入空列表 */
+ InstructionFactory.loadConstInto(ctx, dest, new IRConstant(java.util.List.of()));
+
+ /* 2. 创建参数并写入列表,同时缓存参数寄存器 */
+ List argRegs = new ArrayList<>();
+ for (int i = 0; i < node.arguments().size(); i++) {
+ IRVirtualRegister argReg = build(node.arguments().get(i));
+ argRegs.add(argReg);
+
+ IRVirtualRegister idxReg = ctx.newTempRegister();
+ InstructionFactory.loadConstInto(ctx, idxReg, new IRConstant(i));
+
+ ctx.addInstruction(new CallInstruction(
+ null, "__setindex_r",
+ List.of(dest, idxReg, argReg)));
+ }
+
+ /* 3. 若为结构体实例,调用构造器 .__init__N */
+ if (IRBuilderScope.getStructLayout(node.typeName()) != null) {
+ String ctorName = node.typeName() + ".__init__" + argRegs.size();
+ List ctorArgs = new ArrayList<>();
+ ctorArgs.add(dest); // 隐式 this
+ ctorArgs.addAll(argRegs); // 构造实参
+ ctx.addInstruction(new CallInstruction(null, ctorName, ctorArgs));
+ }
+
+ return dest;
+ }
+
+
+ /**
+ * 构建函数或方法调用表达式的 IR 指令,并返回结果寄存器。
+ *
+ * 支持五类调用:
+ *
+ * 普通函数调用(foo(a, b))
+ * 成员/模块静态方法调用(obj.method(...) 或 Module.func(...))
+ * 链式方法调用(obj.get().foo())
+ * super(...) 调父类构造函数
+ * super.method(...) 调父类实例方法 (新增)
+ *
+ * 核心流程:
+ *
+ * 递归生成所有参数的虚拟寄存器列表
+ * 根据 callee 类型区分结构体方法/模块静态函数/普通函数
+ * 规范化被调用方法名,并整理最终参数表
+ * 分配用于结果的新寄存器,生成 Call 指令
+ *
+ *
+ * @param call 函数或方法调用表达式节点
+ * @return 存放调用结果的目标虚拟寄存器
+ * @throws IllegalStateException 被调用表达式类型不支持或参数异常
*/
private IRVirtualRegister buildCall(CallExpressionNode call) {
- // 1. 递归生成所有参数的寄存器
- List argv = call.arguments().stream().map(this::build).toList();
-
- // 2. 规范化被调用方法名(区分成员方法与普通函数)
- String callee = switch (call.callee()) {
- // 成员方法调用,如 obj.method()
- case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
- // 普通函数调用,或处理命名空间前缀(如当前方法名为 namespace.func)
+ // 1. 递归生成显式实参
+ List explicitRegs = new ArrayList<>();
+ for (ExpressionNode arg : call.arguments()) explicitRegs.add(build(arg));
+
+ String callee; // 规范化后的被调函数名
+ List finalArgs = new ArrayList<>(); // 最终 CALL 参数
+
+ // 2. 根据 callee 类型分支
+ switch (call.callee()) {
+ case IdentifierNode id when "super".equals(id.name()) -> {
+ // ========= 情况 0: super(...) 调用父类构造函数 =========
+ String thisType = ctx.getScope().lookupType("this");
+ if (thisType == null)
+ throw new IllegalStateException("super(...) 只能在构造函数中使用");
+
+ // 查找父类名
+ String parent = IRBuilderScope.getStructParent(thisType);
+ if (parent == null)
+ throw new IllegalStateException(thisType + " 没有父类,无法调用 super(...)");
+
+ // 拼接父类构造函数名,例如 Person.__init__1
+ callee = parent + ".__init__" + explicitRegs.size();
+
+ // 参数表:第一个是 this,再加上传入的实参
+ IRVirtualRegister thisReg = ctx.getScope().lookup("this");
+ if (thisReg == null)
+ throw new IllegalStateException("未绑定 this,不能调用 super(...)");
+
+ finalArgs.add(thisReg);
+ finalArgs.addAll(explicitRegs);
+ }
+ case MemberExpressionNode m when m.object() instanceof IdentifierNode idObj -> {
+ // ========= 情况 1: obj.method(...) 或 ModuleName.func(...) =========
+ String recvName = idObj.name();
+
+ // super.method(...)
+ if ("super".equals(recvName)) {
+ // super.method(...) 调父类实例方法
+ String thisType = ctx.getScope().lookupType("this");
+ if (thisType == null)
+ throw new IllegalStateException("super.method(...) 只能在实例方法中使用");
+
+ String parent = IRBuilderScope.getStructParent(thisType);
+ if (parent == null)
+ throw new IllegalStateException(thisType + " 没有父类,无法调用 super.method(...)");
+
+ callee = parent + "." + m.member();
+
+ IRVirtualRegister thisReg = ctx.getScope().lookup("this");
+ if (thisReg == null)
+ throw new IllegalStateException("未绑定 this");
+
+ finalArgs.add(thisReg); // 隐式 this
+ finalArgs.addAll(explicitRegs); // 显式参数
+ } else {
+ // 普通 obj.method(...) 或 Module.func(...)
+ String recvType = ctx.getScope().lookupType(recvName);
+
+ if (recvType == null || recvType.isEmpty()) {
+ // 模块函数调用 —— "模块名.函数名"
+ callee = recvName + "." + m.member();
+ finalArgs.addAll(explicitRegs);
+ } else {
+ // 结构体实例方法调用 —— "类型名.方法名",且第一个参数为 this
+ callee = recvType + "." + m.member();
+ IRVirtualRegister thisReg = ctx.getScope().lookup(recvName);
+ if (thisReg == null)
+ throw new IllegalStateException("Undefined identifier: " + recvName);
+ finalArgs.add(thisReg); // 隐式 this
+ finalArgs.addAll(explicitRegs); // 其它参数
+ }
+ }
+ }
+
+ /* ===== 递归链式调用 ===== */
+ case MemberExpressionNode m -> {
+ // ========= 情况 2: 通用成员调用 (支持链式调用) =========
+ IRVirtualRegister objReg = build(m.object()); // 递归计算 object 表达式
+
+ callee = m.member(); // 直接用成员名
+ finalArgs.add(objReg); // 隐式 this
+ finalArgs.addAll(explicitRegs); // 其它参数
+ }
+
+ /* ===== 普通函数 foo(...) ===== */
case IdentifierNode id -> {
- String current = ctx.getFunction().name();
+ // ========= 情况 3: 普通函数调用 foo(...) =========
+ String current = ctx.getFunction().name(); // 当前函数全名
+
int dot = current.lastIndexOf('.');
- if (dot > 0)
- yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
- yield id.name(); // 全局函数调用
+ if (dot >= 0 && !id.name().contains(".")) {
+ // 自动补充模块名前缀
+ callee = current.substring(0, dot) + "." + id.name();
+ } else {
+ callee = id.name();
+ }
+ finalArgs.addAll(explicitRegs);
}
- // 其它类型不支持
+
+ /* ===== 其它不支持 ===== */
default -> throw new IllegalStateException(
- "不支持的调用目标: " + call.callee().getClass().getSimpleName());
- };
+ "Unsupported callee type: " + call.callee().getClass().getSimpleName());
+ }
- // 3. 分配用于存放返回值的新寄存器,并生成 Call 指令
+ // 3. 分配目标寄存器,生成函数/方法调用指令
IRVirtualRegister dest = ctx.newRegister();
- ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
+ ctx.addInstruction(new CallInstruction(dest, callee, finalArgs));
return dest;
}
-
/**
* 二元表达式构建,结果存储到新寄存器。
*
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java
index 2fb428ff5f3155b9aa69dda152ec35d1b31e42fc..2aed3a53bcf2848d32653385f4f09663b14b91f8 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java
@@ -12,14 +12,15 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
/**
* IR 函数构建器。
*
- * 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction,
- * 包含参数声明、返回类型推断、函数体语句转换等步骤。
- *
+ * 将语法树中的 {@link FunctionNode} 转换为可执行的 {@link IRFunction},包含:
*
- * 支持自动导入全局/跨模块常量,使跨模块常量引用(如 ModuleA.a)在 IR 阶段可用。
- * 将函数形参声明为虚拟寄存器,并注册到作用域,便于后续指令生成。
- * 根据返回类型设置表达式默认字面量类型,保证 IR 层类型一致性。
- * 遍历并转换函数体语句为 IR 指令。
+ * 在全局函数表登记函数名与返回类型;
+ * 初始化 IR 容器与构建上下文(作用域、寄存器池等);
+ * 导入全局/跨模块常量,便于常量折叠与跨模块引用;
+ * 依据返回类型为表达式设置数字字面量默认后缀;
+ * 分配形参寄存器并注册到作用域与 IR 函数;
+ * 将函数体语句逐条转为 IR 指令;
+ * 在构建完成后清理默认字面量后缀,避免泄漏到其它函数。
*
*/
public class FunctionBuilder {
@@ -44,57 +45,56 @@ public class FunctionBuilder {
* @return 构建得到的 IRFunction 对象
*/
public IRFunction build(FunctionNode functionNode) {
-
- // 1. 在全局函数表注册函数名与返回类型
- // 方便其他阶段/模块调用、类型检查。
+ // 1) 在全局函数表登记:名称 + 返回类型(返回类型可能为 null,例如构造函数 init)
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
- // 2. 初始化 IRFunction 实例与上下文对象
- // IRFunction: 表示该函数的中间代码容器
- // IRContext: 负责作用域、寄存器分配等编译上下文管理
+ // 2) 初始化 IR 容器与上下文
IRFunction irFunction = new IRFunction(functionNode.name());
IRContext irContext = new IRContext(irFunction);
- // 3. 自动导入所有全局/跨模块常量到当前作用域
- // 支持如 ModuleA.a 这样的常量访问/折叠(参见 ExpressionBuilder)
+ // 3) 导入所有全局/跨模块常量到当前作用域(便于常量折叠和跨模块引用)
GlobalConstTable.all().forEach((k, v) ->
irContext.getScope().importExternalConst(k, v));
- // 4. 根据函数返回类型设置默认类型后缀
- // 例如返回类型为 double 时, 字面量表达式自动用 d 后缀。
- char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
+ // 4) 根据函数返回类型设置“数字字面量默认后缀”
+ // - 关键修复:对 returnType 进行空值/空白保护,缺省视为 "void"
+ String rt = functionNode.returnType();
+ String rtLower = (rt == null || rt.trim().isEmpty()) ? "void" : rt.trim().toLowerCase();
+
+ // 根据返回类型决定默认字面量后缀
+ // 仅在浮点/整型长短类型上设置;其它/void 情况不设置(使用 '\0' 表示不设置)
+ char defaultSuffix = switch (rtLower) {
case "double" -> 'd';
case "float" -> 'f';
case "long" -> 'l';
case "short" -> 's';
case "byte" -> 'b';
- default -> '\0'; // 其它类型不设默认后缀
+ default -> '\0';
};
- ExpressionUtils.setDefaultSuffix(_returnSuffix);
+ ExpressionUtils.setDefaultSuffix(defaultSuffix);
try {
- // 5. 遍历函数参数列表
- // - 为每个参数分配一个新的虚拟寄存器
- // - 注册参数名、类型、寄存器到当前作用域
- // - 添加参数寄存器到 IRFunction(用于后续调用与指令生成)
+ // 5) 处理形参:
+ // - 为每个形参分配一个新的虚拟寄存器(从 IRContext 统一分配,保证作用域一致)
+ // - 将 (参数名, 类型, 寄存器) 声明到当前作用域
+ // - 将寄存器加入 IRFunction 的参数列表,便于后续调用/生成
for (ParameterNode p : functionNode.parameters()) {
- IRVirtualRegister reg = irFunction.newRegister();
+ IRVirtualRegister reg = irContext.newRegister(); // 使用上下文统一分配
irContext.getScope().declare(p.name(), p.type(), reg);
irFunction.addParameter(reg);
}
- // 6. 遍历函数体语句节点,转换为 IR 指令
- // StatementBuilder 负责将每条语句递归转换为 IR
+ // 6) 构建函数体:将每条语句节点转换为 IR 指令序列
StatementBuilder stmtBuilder = new StatementBuilder(irContext);
for (StatementNode stmt : functionNode.body()) {
stmtBuilder.build(stmt);
}
} finally {
- // 7. 清理默认类型后缀,防止影响后续其他函数的类型推断
+ // 7) 清理默认后缀,防止影响后续函数的字面量推断
ExpressionUtils.clearDefaultSuffix();
}
- // 8. 返回构建完成的 IRFunction
+ // 8) 返回构建完成的 IRFunction
return irFunction;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java
index 7d277cab02c5569b071804cb70da82b2d7583590..4739b70ba6b8f5441525f52baba8d8a6bca6d8cd 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRBuilderScope.java
@@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -16,9 +17,11 @@ import java.util.Map;
* 支持变量的类型信息记录与查询
* 支持变量的编译期常量值记录与查询(便于常量折叠等优化)
* 支持跨模块全局常量(如 ModuleA.a)查找
+ * 维护结构体字段布局(全局共享):字段名 → 槽位下标,用于对象字段读写
+ * 维护结构体继承关系(子类 → 父类),super(...) 调用会用到
*
*/
-final class IRBuilderScope {
+public final class IRBuilderScope {
/** 变量名到虚拟寄存器的映射表(本地变量) */
private final Map vars = new HashMap<>();
@@ -27,15 +30,20 @@ final class IRBuilderScope {
/** 变量名到编译期常量值的映射表(本作用域) */
private final Map varConstValues = new HashMap<>();
- /**
- * 额外:存放跨模块导入的全局常量
- * key 形如 "ModuleA.a" value 为其常量值
- */
+ /** 存放跨模块导入的全局常量(如 ModuleA.a) */
private final Map externalConsts = new HashMap<>();
- /** 当前作用域所绑定的 IRFunction 实例 */
+ /** 结构体字段布局表:结构体名 → (字段名 → 槽位下标) */
+ private static final Map> STRUCT_LAYOUTS = new HashMap<>();
+
+ /** 结构体继承关系表:子类名 → 父类名 */
+ private static final Map STRUCT_PARENTS = new HashMap<>();
+
+ /** 当前作用域所绑定的 IRFunction 实例,用于变量分配新寄存器等。 */
private IRFunction fn;
+ // ---------------- 作用域与变量 ----------------
+
/**
* 绑定当前作用域到指定 IRFunction。
*
@@ -55,7 +63,7 @@ final class IRBuilderScope {
IRVirtualRegister reg = fn.newRegister();
vars.put(name, reg);
varTypes.put(name, type);
- varConstValues.remove(name);
+ varConstValues.remove(name); // 声明新变量即清除原常量绑定
}
/**
@@ -68,7 +76,7 @@ final class IRBuilderScope {
void declare(String name, String type, IRVirtualRegister reg) {
vars.put(name, reg);
varTypes.put(name, type);
- varConstValues.remove(name);
+ varConstValues.remove(name); // 重复声明也会清除常量绑定
}
/**
@@ -104,16 +112,18 @@ final class IRBuilderScope {
/**
* 获取变量名到类型名映射的不可变副本。
*
- * @return 变量名→类型名映射的只读视图
+ * @return 变量名→类型名映射的只读视图(用于调试/全局分析)
*/
Map getVarTypes() {
- return Map.copyOf(varTypes);
+ return Collections.unmodifiableMap(varTypes);
}
- // ---------------- 编译期常量相关接口 ----------------
+ // ---------------- 编译期常量 ----------------
/**
* 设置变量的编译期常量值(本地变量)。
+ *
+ * 便于 IR 生成时做常量折叠等优化,value 传 null 则清除绑定。
*
* @param name 变量名称
* @param value 常量值(null 表示清除)
@@ -124,9 +134,9 @@ final class IRBuilderScope {
}
/**
- * 获取变量的编译期常量值(本地变量或导入的外部常量)。
- *
- * 优先查找本地常量,未命中再查外部(如 "ModuleA.a")。
+ * 获取变量的编译期常量值(优先本地,再查跨模块导入)。
+ *
+ * 常用于优化与折叠,支持 "Module.a" 这类跨模块全局常量查找。
*
* @param name 变量名称或"模块名.常量名"
* @return 编译期常量值,或 null
@@ -134,7 +144,7 @@ final class IRBuilderScope {
Object getConstValue(String name) {
Object v = varConstValues.get(name);
if (v != null) return v;
- // 支持跨模块常量/全局变量
+ // 支持跨模块常量/全局变量(如 "ModuleA.a")
return externalConsts.get(name);
}
@@ -158,4 +168,119 @@ final class IRBuilderScope {
void importExternalConst(String qualifiedName, Object value) {
externalConsts.put(qualifiedName, value);
}
+
+ // ---------------- 结构体字段布局(全局静态) ----------------
+
+ /**
+ * 全局注册结构体的字段布局映射(字段名 -> 槽位下标)。
+ * 一般在语义分析/IR 前期由类型系统收集后调用。
+ *
+ * @param structName 结构体名(建议为简单名,如 "Animal";如有模块前缀也可)
+ * @param fieldToIndex 字段名到下标的映射(下标从 0 递增)
+ */
+ static void registerStructLayout(String structName, Map fieldToIndex) {
+ if (structName == null || fieldToIndex == null) return;
+ // 覆盖式注册:方便增量/重复编译时刷新
+ STRUCT_LAYOUTS.put(structName, new HashMap<>(fieldToIndex));
+ }
+
+ /**
+ * 查询字段槽位下标。
+ * 支持“模块.结构体”及简单名两种写法自动兼容。
+ *
+ * @param structName 结构体名("Module.Struct" 或 "Struct")
+ * @param fieldName 字段名
+ * @return 槽位下标;若未知返回 null
+ */
+ Integer lookupFieldIndex(String structName, String fieldName) {
+ // 先按原样查
+ Map layout = STRUCT_LAYOUTS.get(structName);
+ // 兼容 “模块.结构体” 的写法:若没命中,退化为简单名再查
+ if (layout == null && structName != null) {
+ int dot = structName.lastIndexOf('.');
+ if (dot >= 0 && dot + 1 < structName.length()) {
+ layout = STRUCT_LAYOUTS.get(structName.substring(dot + 1));
+ }
+ }
+ if (layout == null) return null;
+ return layout.get(fieldName);
+ }
+
+ /**
+ * 读取某结构体的完整字段布局(返回只读 Map)。
+ * 支持“模块.结构体”及简单名两种写法。
+ *
+ * @param structName 结构体名
+ * @return 字段名到下标映射的只读视图,或 null
+ */
+ static Map getStructLayout(String structName) {
+ Map layout = STRUCT_LAYOUTS.get(structName);
+ if (layout == null && structName != null) {
+ int dot = structName.lastIndexOf('.');
+ if (dot >= 0 && dot + 1 < structName.length()) {
+ layout = STRUCT_LAYOUTS.get(structName.substring(dot + 1));
+ }
+ }
+ return layout == null ? null : Collections.unmodifiableMap(layout);
+ }
+
+ // ---------------- 结构体继承关系 ----------------
+ /**
+ * 注册结构体的父类信息。
+ *
+ * 该方法用于维护结构体与其父类之间的映射关系。通过调用此方法,
+ * 可以为某个结构体名称指定其父结构体名称,并保存到全局的
+ * {@code STRUCT_PARENTS} 映射中。
+ *
+ *
+ * @param structName 子结构体的名称,不能为空或 {@code null}
+ * @param parentName 父结构体的名称,不能为空、不能为 {@code null} 或空白
+ */
+ static void registerStructParent(String structName, String parentName) {
+ if (structName == null || parentName == null || parentName.isBlank()) return;
+ STRUCT_PARENTS.put(structName, parentName);
+ }
+
+ /**
+ * 获取某个结构体的父结构体名称。
+ *
+ * 查询逻辑如下:
+ *
+ * 首先直接从 {@code STRUCT_PARENTS} 中查找对应的父结构体。
+ * 如果未找到,且 {@code structName} 含有点号(.),则提取点号后的简单类名进行查找。
+ * 如果仍未找到,则遍历 {@code STRUCT_PARENTS} 的所有条目:
+ *
+ * 检查键(key)是否包含点号,若存在,则取其最后一个点号后的子串作为简单类名。
+ * 如果该简单类名与传入的 {@code structName} 相等,则返回对应的父类名称。
+ *
+ *
+ * 如果所有方式都未找到,则返回 {@code null}。
+ *
+ *
+ *
+ * @param structName 需要查找父类的结构体名称,可以是完整类名或简单类名
+ * @return 父结构体名称;如果未找到,返回 {@code null}
+ */
+ public static String getStructParent(String structName) {
+ if (structName == null || structName.isBlank()) return null;
+ String p = STRUCT_PARENTS.get(structName);
+ if (p != null) return p;
+
+ int dot = structName.lastIndexOf('.');
+ if (dot >= 0 && dot + 1 < structName.length()) {
+ String simple = structName.substring(dot + 1);
+ p = STRUCT_PARENTS.get(simple);
+ if (p != null) return p;
+ }
+
+ for (Map.Entry e : STRUCT_PARENTS.entrySet()) {
+ String k = e.getKey();
+ int d = k.lastIndexOf('.');
+ if (d >= 0 && d + 1 < k.length() && k.substring(d + 1).equals(structName)) {
+ return e.getValue();
+ }
+ }
+ return null;
+ }
+
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java
index ca870c09706092d9be4f27b0edb77a78a5ecbb0b..e931ff5cf3a64539ba54461e98eb506ef456f8ec 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java
@@ -6,114 +6,102 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
/**
- * IRContext 类负责封装当前正在构建的 IRFunction 实例
+ * {@code IRContext} 类负责封装当前正在构建的 IRFunction 实例
* 以及与之配套的作用域管理(IRBuilderScope),
* 并简化虚拟寄存器分配与 IR 指令添加操作。
*
- * 本类提供以下核心功能:
+ *
本类提供以下核心功能:
*
* 持有并操作当前 IRFunction 对象;
* 管理变量名与虚拟寄存器的映射关系;
* 分配新的虚拟寄存器实例;
* 将生成的 IRInstruction 自动添加到 IRFunction 中;
+ * 支持声明阶段临时类型记录(如变量声明时的类型推断/校验);
*
+ * 注: 该类一般不直接暴露给最终用户,仅供 IR 构建器内部流程调用。
*/
public class IRContext {
- /* 生成唯一标签用 */
+ /** label 自动生成计数器,用于保证唯一性 */
private int labelCounter = 0;
- /**
- * 当前正在构建的 IRFunction 对象,所有指令将添加至此
- */
+
+ /** 当前正在构建的 IRFunction 对象,所有 IR 指令均将添加至此 */
private final IRFunction function;
- /**
- * 用于管理当前函数作用域内变量与虚拟寄存器的映射
- */
+ /** 用于管理当前函数作用域内变量与虚拟寄存器、类型等的映射关系 */
private final IRBuilderScope scope;
- /**
- * 当前声明变量的类型,不在声明变量时为空
- */
+ /** 当前 declare 编译阶段变量类型,不在变量声明流程时为 null */
private String varType;
/**
- * 构造一个新的 IRContext,并将指定的 IRFunction 与作用域关联。
+ * 构造一个新的 IRContext,并将指定的 IRFunction 与作用域管理器关联。
*
- * @param function 要构建的 IRFunction 实例
+ * @param function 要构建的 IRFunction 实例(不可为 null)
*/
public IRContext(IRFunction function) {
this.function = function;
this.scope = new IRBuilderScope();
- // 关联作用域与 IRFunction,以便在声明变量时申请寄存器
+ // 作用域需知晓当前 IRFunction,以便为变量分配寄存器
this.scope.attachFunction(function);
this.varType = null;
}
- /**
- * 获取当前正在构建的 IRFunction 对象。
- *
- * @return 当前 IRFunction 实例
- */
+ /** 获取当前正在构建的 IRFunction 对象。 */
public IRFunction getFunction() {
return function;
}
/**
* 获取当前函数的变量与寄存器映射作用域。
- *
- * 包内可见: 仅限 builder 包内部使用。
- *
- * @return IRBuilderScope 实例
+ *
包内可见,仅限 builder 包内部调用,避免被外部滥用。
*/
IRBuilderScope getScope() {
return scope;
}
- /**
- * 为当前函数分配一个新的虚拟寄存器。
- *
- * @return 分配到的 IRVirtualRegister 对象
- */
+ /** 为当前函数分配一个新的虚拟寄存器。 */
public IRVirtualRegister newRegister() {
return function.newRegister();
}
/**
- * 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中。
- *
- * @param instr 要添加的 IRInstruction 实例
+ * 兼容用法:分配一个“临时虚拟寄存器”。
+ *
+ * 目前直接委托给 {@link #newRegister()},便于老代码兼容与简单用法。
+ * 若将来有命名/临时寄存器区分的需要,可在此扩展实现。
+ *
*/
+ public IRVirtualRegister newTempRegister() {
+ return newRegister();
+ }
+
+ /** 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中。 */
public void addInstruction(IRInstruction instr) {
function.add(instr);
}
- /** 生成一个形如 L0 / L1 ... 的唯一标签名 */
+ /**
+ * 生成一个唯一标签名,如 L0、L1、L2...
+ * 常用于条件跳转、分支合流等 IR 控制流构建场景。
+ *
+ * @return 形如 "L0", "L1" 等的唯一字符串标签
+ */
public String newLabel() {
return "L" + (labelCounter++);
}
- /**
- * 获取当前 declare 编译阶段变量类型
- *
- * @return 当前 declare 的变量类型
- */
+ /** 获取当前 declare 编译阶段变量类型(声明流程中临时记录) */
public String getVarType() {
return varType;
}
- /**
- * 设置当前 declare 编译阶段变量类型
- *
- */
+ /** 设置当前 declare 编译阶段变量类型(一般在变量声明时赋值) */
public void setVarType(String type) {
this.varType = type;
}
- /**
- * 清除当前 declare 编译阶段变量类型
- *
- */
+ /** 清除当前 declare 编译阶段变量类型(声明流程结束时调用) */
public void clearVarType() {
this.varType = null;
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java
index 730b731568f69f2eca8703bfd8b9175af38ea44f..831028843603017244ff77f7f54edc52aeba2f66 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRProgramBuilder.java
@@ -9,62 +9,212 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
/**
- * IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram}。
+ * IRProgramBuilder 负责将 AST 顶层节点(如模块、函数、语句等)转换为可执行的 {@link IRProgram}。
*
+ *
+ * 主要工作内容包括:
+ *
*
* 预扫描所有模块,将 declare const
常量登记到全局常量表,支持跨模块常量折叠。
- * 对模块内的函数加上模块前缀,保证命名唯一,并将本模块全局声明注入到函数体前部。
+ * 预扫描并注册所有 struct 的字段布局(字段→下标),供 IR 阶段对象字段读写使用。
+ * 对模块内的普通函数加上模块前缀(ModuleName.func),保证命名唯一,并将本模块全局声明注入到函数体前部。
+ * 将 struct 的 init 与 methods 降级为普通 IR 函数并注册为 StructName.__init__
、StructName.method
;同时在其参数列表首位插入隐式 this: StructName
。
* 将独立顶层语句自动包装为特殊的 "_start" 函数(脚本模式支持)。
*
+ *
+ *
+ * 该类为不可变工具类,仅包含静态行为,不持有状态。
+ *
*/
public final class IRProgramBuilder {
/**
* 将解析生成的 AST 根节点列表转换为 IRProgram。
*
- * @param roots 含 ModuleNode、FunctionNode 或 StatementNode 的顶层 AST 根节点列表
- * @return 包含所有转换后 IRFunction 的 IRProgram 对象
+ * @param roots 顶层 AST 根节点列表(ModuleNode / FunctionNode / StatementNode)
+ * @return 构建好的 IRProgram
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
+ *
+ * 主流程:
+ *
+ * 登记全局常量(便于后续常量折叠)。
+ * 注册所有 struct 的字段布局(为成员读写和 this.xx 做准备)。
+ * 遍历根节点:模块 → 先降级并注册 struct 的构造/方法,再处理模块函数;顶层函数直接构建;顶层语句打包为 "_start" 构建。
+ *
*/
public IRProgram buildProgram(List roots) {
- // 预先收集并登记全部模块常量到全局常量表
+ // 1. 先登记全局常量,便于后续常量折叠
preloadGlobals(roots);
+ // 2. 注册所有结构体的字段布局(为成员访问做准备)
+ preloadStructLayouts(roots);
+ // 创建 IR 程序对象
IRProgram irProgram = new IRProgram();
+ // 3. 遍历并处理所有顶层节点
for (Node node : roots) {
switch (node) {
case ModuleNode moduleNode -> {
- // 处理模块节点:遍历其中所有函数,统一用“模块名.函数名”作为全限定名,避免命名冲突
- for (FunctionNode f : moduleNode.functions()) {
- irProgram.add(buildFunctionWithGlobals(moduleNode, f));
+ // 3.1 先降级并注册本模块所有 struct 的构造/方法(struct 方法降级)
+ if (moduleNode.structs() != null) {
+ for (StructNode structNode : moduleNode.structs()) {
+ lowerAndRegisterStruct(structNode, irProgram);
+ }
+ }
+ // 3.2 再处理模块里的普通函数,模块内函数名全限定,注入全局声明
+ if (moduleNode.functions() != null) {
+ for (FunctionNode f : moduleNode.functions()) {
+ irProgram.add(buildFunctionWithGlobals(moduleNode, f));
+ }
}
}
case FunctionNode functionNode ->
- // 处理顶层函数节点:直接构建为 IRFunction 并加入
+ // 3.3 处理顶层函数节点:直接构建为 IRFunction 并加入
irProgram.add(buildFunction(functionNode));
case StatementNode statementNode ->
- // 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
+ // 3.4 处理脚本式顶层语句:封装成 "_start" 函数后构建并添加
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
default ->
- // 遇到未知类型节点,抛出异常
+ // 3.5 遇到未知类型节点,抛出异常
throw new IllegalStateException("Unsupported top-level node: " + node);
}
}
return irProgram;
}
- // ===================== 全局常量收集 =====================
+ // ===================== 预扫描:注册结构体字段布局 =====================
+
+ /**
+ * 为每个结构体注册字段布局(字段名 → 槽位索引),并在有继承时将父类布局复制并续接。
+ *
+ * 规则:
+ *
+ * 若存在父类:先取到父类布局(如果能找到),将其按顺序复制到当前布局中;起始索引 = 父类字段数。
+ * 再将当前结构体声明的字段按声明顺序追加;如果字段名与父类重复,跳过以避免覆盖。
+ * 最后将布局以 StructName
为键注册到 {@link IRBuilderScope} 的全局布局表。
+ * 同时调用 {@link IRBuilderScope#registerStructParent} 登记继承关系(子类 → 父类)。
+ *
+ * @param roots AST 顶层节点列表,包含模块/结构体信息
+ */
+ private void preloadStructLayouts(List roots) {
+ for (Node n : roots) {
+ if (!(n instanceof ModuleNode mod)) continue;
+ if (mod.structs() == null) continue;
+
+ for (StructNode s : mod.structs()) {
+ List fields = s.fields();
+ Map layout = new LinkedHashMap<>();
+ int idx = 0;
+
+ // 1. 若有父类,先复制父类布局,并将索引起点置为父类字段数
+ String parentName = s.parent();
+ if (parentName != null && !parentName.isBlank()) {
+ // 注册继承关系,供 super(...) 调用解析
+ IRBuilderScope.registerStructParent(s.name(), parentName);
+
+ Map parentLayout = IRBuilderScope.getStructLayout(parentName);
+ if (parentLayout != null && !parentLayout.isEmpty()) {
+ layout.putAll(parentLayout);
+ idx = parentLayout.size();
+ }
+ }
+
+ // 2. 续接当前结构体声明的字段;若与父类同名,跳过(避免覆盖父类槽位)
+ if (fields != null) {
+ for (DeclarationNode d : fields) {
+ String name = d.getName();
+ if (!layout.containsKey(name)) {
+ layout.put(name, idx++);
+ }
+ }
+ }
+
+ // 3. 注册最终布局
+ IRBuilderScope.registerStructLayout(s.name(), layout);
+ }
+ }
+ }
+
+
+ // ===================== Struct 降级:方法/构造 → 普通函数 =====================
/**
- * 扫描所有模块节点,将其中声明的 const 全局变量(compile-time 常量)
+ * 将一个 Struct 的所有构造函数(inits)和方法(methods)降级为普通 Function,并注册进 IRProgram:
+ *
+ * 构造函数:StructName.__init__N(this:StructName, ...),N为参数个数
+ * 方法:StructName.method(this:StructName, ...)
+ *
+ * 降级规则:
+ *
+ * 构造/方法函数名前缀加上结构体名(便于唯一定位)。
+ * 参数列表最前添加隐式 this:StructName 参数。
+ *
+ *
+ * @param structNode 当前结构体节点
+ * @param out 注册到的 IRProgram
+ */
+ private void lowerAndRegisterStruct(StructNode structNode, IRProgram out) {
+ String structName = structNode.name();
+
+ // 1. 多构造函数:降级为 StructName.__init__N
+ if (structNode.inits() != null) {
+ for (FunctionNode initFn : structNode.inits()) {
+ String loweredName = structName + ".__init__" + initFn.parameters().size();
+ FunctionNode loweredInit = lowerStructCallable(
+ initFn,
+ loweredName,
+ structName
+ );
+ out.add(buildFunction(loweredInit));
+ }
+ }
+
+ // 2. 降级处理所有普通方法
+ if (structNode.methods() != null) {
+ for (FunctionNode m : structNode.methods()) {
+ FunctionNode loweredMethod = lowerStructCallable(
+ m,
+ structName + "." + m.name(),
+ structName
+ );
+ out.add(buildFunction(loweredMethod));
+ }
+ }
+ }
+
+ /**
+ * 生成一个带隐式 this:StructName 参数的函数节点副本,并重命名为 loweredName。
+ *
+ * @param original 原始 FunctionNode
+ * @param loweredName 降级后的新函数名(如 StructName.method)
+ * @param structName 结构体名,用于 this 参数类型
+ * @return 重新包装后的函数节点
+ */
+ private FunctionNode lowerStructCallable(FunctionNode original, String loweredName, String structName) {
+ // 在参数列表首位插入隐式 this 参数(类型为结构体名)
+ List newParams = new ArrayList<>(original.parameters().size() + 1);
+ newParams.add(new ParameterNode("this", structName, original.context()));
+ newParams.addAll(original.parameters());
+
+ return new FunctionNode(
+ loweredName,
+ newParams,
+ original.returnType(),
+ original.body(),
+ original.context()
+ );
+ }
+
+ // ===================== 预扫描:全局常量收集 =====================
+
+ /**
+ * 扫描所有模块节点,将其中声明的 const 全局变量(即编译期常量)
* 以 "模块名.常量名" 形式注册到全局常量表。
- * 支持跨模块常量折叠,用于后续 IR 生成过程中的常量折叠优化。
+ *
+ * 支持跨模块常量折叠,便于 IR 生成时做常量传播与优化。
+ *
*
* @param roots AST 顶层节点列表
*/
@@ -74,7 +224,7 @@ public final class IRProgramBuilder {
String moduleName = mod.name();
if (mod.globals() == null) continue;
for (DeclarationNode decl : mod.globals()) {
- // 只处理 compile-time 的 const 常量,并要求有初始值
+ // 只处理带初始值的 const 常量(编译期常量),忽略 run-time/无初始值
if (!decl.isConst() || decl.getInitializer().isEmpty()) continue;
ExpressionNode init = decl.getInitializer().get();
Object value = evalLiteral(init);
@@ -88,24 +238,28 @@ public final class IRProgramBuilder {
/**
* 字面量提取与类型折叠工具。
+ *
* 用于将表达式节点还原为 Java 原生类型(int、long、double、String等),仅支持直接字面量。
+ * 不支持复杂表达式、非常量等情况,无法静态折叠则返回 null。
*
- * @param expr 要计算的表达式节点
- * @return 提取到的原生常量值,不支持的情况返回 null
+ * @param expr 要计算的表达式节点(要求是字面量)
+ * @return 提取到的原生常量值;若不支持则返回 null
*/
private Object evalLiteral(ExpressionNode expr) {
return switch (expr) {
case NumberLiteralNode num -> {
+ // 数字字面量:支持下划线、类型后缀(如 123_456L)
String raw = num.value();
String s = raw.replace("_", "");
char last = Character.toLowerCase(s.charAt(s.length() - 1));
+ // 处理类型后缀:如 123l/123d/123f
String core = switch (last) {
case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
default -> s;
};
try {
+ // 支持浮点数、科学计数法
if (core.contains(".") || core.contains("e") || core.contains("E")) {
- // 浮点数处理
yield Double.parseDouble(core);
}
long lv = Long.parseLong(core);
@@ -119,9 +273,9 @@ public final class IRProgramBuilder {
yield null;
}
}
- case StringLiteralNode str -> str.value();
- case BoolLiteralNode b -> b.getValue() ? 1 : 0;
- default -> null;
+ case StringLiteralNode str -> str.value(); // 字符串字面量直接返回
+ case BoolLiteralNode b -> b.getValue() ? 1 : 0; // 布尔常量转为 1/0
+ default -> null; // 其他情况不支持常量折叠
};
}
@@ -129,21 +283,23 @@ public final class IRProgramBuilder {
/**
* 构建带有模块全局声明“注入”的函数,并将函数名加上模块前缀,保证模块内函数名唯一。
- * 如果模块有全局声明,则这些声明会被插入到函数体前部(**会过滤掉与参数同名的全局声明**)。
+ *
+ * 如果模块有全局声明,则这些声明会被插入到函数体前部(会过滤掉与参数同名的全局声明 ,防止变量遮蔽)。
+ *
*
* @param moduleNode 所属模块节点
* @param functionNode 待构建的函数节点
* @return 包含全局声明的 IRFunction
*/
private IRFunction buildFunctionWithGlobals(ModuleNode moduleNode, FunctionNode functionNode) {
- // 拼接模块名和函数名,生成全限定名
+ // 1. 拼接模块名和函数名,生成全限定名
String qualifiedName = moduleNode.name() + "." + functionNode.name();
+ // 2. 若无全局声明,直接重命名构建
if (moduleNode.globals() == null || moduleNode.globals().isEmpty()) {
- // 无全局声明,直接重命名构建
return buildFunction(renameFunction(functionNode, qualifiedName));
}
- // ------- 过滤与参数重名的全局声明 -------
+ // 3. 过滤掉与参数重名的全局声明(优先参数作用域,避免变量遮蔽)
Set paramNames = new HashSet<>();
for (ParameterNode p : functionNode.parameters()) {
paramNames.add(p.name());
@@ -156,12 +312,12 @@ public final class IRProgramBuilder {
}
}
+ // 4. 若无可插入的全局声明,直接重命名构建
if (filteredGlobals.isEmpty()) {
- // 过滤后已无可插入的全局声明
return buildFunction(renameFunction(functionNode, qualifiedName));
}
- // 合并全局声明与函数体,前插全局声明
+ // 5. 合并全局声明与函数体,前插全局声明
List newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size());
newBody.addAll(filteredGlobals);
newBody.addAll(functionNode.body());
@@ -197,6 +353,8 @@ public final class IRProgramBuilder {
*
* @param functionNode 待构建的 FunctionNode
* @return 构建后的 IRFunction
+ *
+ * 本方法仅作中转,直接委托给 FunctionBuilder。
*/
private IRFunction buildFunction(FunctionNode functionNode) {
return new FunctionBuilder().build(functionNode);
@@ -215,6 +373,7 @@ public final class IRProgramBuilder {
List.of(),
"void",
List.of(stmt),
+ // 用(-1,-1,"")占位,避免依赖真实位置信息
new NodeContext(-1, -1, "")
);
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java
index 0d1fa6946e8d16aed58f1ee1faef9e0bf4bf8676..5127f22fc7dad7865fcdeff0a2bd5544d081857a 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java
@@ -104,7 +104,7 @@ public class InstructionFactory {
/**
* 生成“值拷贝”语义(src → dest)。
- * 若类型无法推断,默认采用 int 方案(ADD_I32, src+0)。
+ * 通过在 IR 中构造 “src + 0” 的形式,触发 Peephole 优化折叠成 MOV。
*
* @param ctx 当前 IR 上下文
* @param src 源寄存器
@@ -114,46 +114,50 @@ public class InstructionFactory {
if (src == dest) {
return;
}
- String varType = ctx.getVarType(); // 需要 IRContext 提供
- char suffix = '\0';
+ String varType = ctx.getVarType();
+ IROpCode op;
+ IRConstant zeroConst;
+
if (varType != null) {
switch (varType) {
- case "byte" -> suffix = 'b';
- case "short" -> suffix = 's';
- case "int" -> suffix = 'i';
- case "long" -> suffix = 'l';
- case "float" -> suffix = 'f';
- case "double" -> suffix = 'd';
+ case "byte" -> {
+ op = IROpCode.ADD_B8;
+ zeroConst = new IRConstant((byte) 0);
+ }
+ case "short" -> {
+ op = IROpCode.ADD_S16;
+ zeroConst = new IRConstant((short) 0);
+ }
+ case "int" -> {
+ op = IROpCode.ADD_I32;
+ zeroConst = new IRConstant(0);
+ }
+ case "long" -> {
+ op = IROpCode.ADD_L64;
+ zeroConst = new IRConstant(0L);
+ }
+ case "float" -> {
+ op = IROpCode.ADD_F32;
+ zeroConst = new IRConstant(0.0f);
+ }
+ case "double" -> {
+ op = IROpCode.ADD_D64;
+ zeroConst = new IRConstant(0.0);
+ }
+ default -> {
+ // 引用类型 / 结构体等统一走 int 路径
+ op = IROpCode.ADD_I32;
+ zeroConst = new IRConstant(0);
+ }
}
+ } else {
+ // 无法推断类型时,退化为 int 方案
+ op = IROpCode.ADD_I32;
+ zeroConst = new IRConstant(0);
}
- IRVirtualRegister zero;
- IROpCode op = switch (suffix) {
- case 'd' -> {
- zero = loadConst(ctx, 0.0);
- yield IROpCode.ADD_D64;
- }
- case 'f' -> {
- zero = loadConst(ctx, 0.0f);
- yield IROpCode.ADD_F32;
- }
- case 'l' -> {
- zero = loadConst(ctx, 0L);
- yield IROpCode.ADD_L64;
- }
- case 's' -> {
- zero = loadConst(ctx, 0);
- yield IROpCode.ADD_S16;
- }
- case 'b' -> {
- zero = loadConst(ctx, 0);
- yield IROpCode.ADD_B8;
- }
- default -> {
- zero = loadConst(ctx, 0);
- yield IROpCode.ADD_I32;
- }
- };
- ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zero));
+
+ // 注意:这里传入的是立即数 zeroConst,而不是寄存器
+ ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zeroConst));
}
/**
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
index e95d132161317671db1c33a78cb78d30507f414a..18e9f8656c6fa5b0452395afc43786ada9b38045 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
@@ -24,9 +24,6 @@ import java.util.ArrayDeque;
* 负责控制流跳转(分支、循环)的标签分配与维护。
* 在变量赋值和声明时自动常量折叠和登记。
*
- *
- * @author [你的名字]
- * @since 1.0
*/
public class StatementBuilder {
@@ -84,8 +81,58 @@ public class StatementBuilder {
expr.build(exp);
return;
}
+
+ // ===== 赋值语句 =====
if (stmt instanceof AssignmentNode(String var, ExpressionNode rhs, NodeContext _)) {
+ // 1) 优先当作“已存在的局部/参数变量”赋值(保持原有行为)
final String type = ctx.getScope().lookupType(var);
+ IRVirtualRegister localReg = ctx.getScope().lookup(var);
+ if (localReg != null) {
+ ctx.setVarType(type);
+ expr.buildInto(rhs, localReg);
+
+ // 赋值时尝试记录/清除常量
+ try {
+ Object constVal = tryFoldConst(rhs);
+ if (constVal != null) ctx.getScope().setConstValue(var, constVal);
+ else ctx.getScope().clearConstValue(var);
+ } catch (Throwable ignored) {}
+ ctx.clearVarType();
+ return;
+ }
+
+ // 2) 若不是局部变量:尝试将“裸标识符”视为 this. 的结构体字段赋值
+ IRVirtualRegister thisReg = ctx.getScope().lookup("this");
+ String thisType = ctx.getScope().lookupType("this");
+ if (thisReg != null && thisType != null) {
+ java.util.Map layout = ctx.getScope().getStructLayout(thisType);
+ if (layout == null) {
+ // 兼容 "Module.Struct" 与简单名 "Struct"
+ int dot = thisType.lastIndexOf('.');
+ if (dot >= 0 && dot + 1 < thisType.length()) {
+ layout = ctx.getScope().getStructLayout(thisType.substring(dot + 1));
+ }
+ }
+ Integer idx = (layout != null) ? layout.get(var) : null;
+
+ if (idx != null) {
+ // 生成右值
+ IRVirtualRegister valReg = expr.build(rhs);
+ // 字段槽位下标常量
+ IRVirtualRegister idxReg = InstructionFactory.loadConst(ctx, idx);
+ // 用 __setindex_r 写入字段(与构造写入/读取对齐)
+ java.util.List argv =
+ java.util.List.of(thisReg, idxReg, valReg);
+ ctx.addInstruction(new org.jcnc.snow.compiler.ir.instruction.CallInstruction(
+ null, "__setindex_r", argv));
+
+ // 字段写入后清理该标识符的常量绑定(避免误当作本地常量)
+ try { ctx.getScope().clearConstValue(var); } catch (Throwable ignored) {}
+ return;
+ }
+ }
+
+ // 3) 否则退回为“声明/绑定一个新的局部变量并赋值”(与原逻辑一致)
ctx.setVarType(type);
IRVirtualRegister target = getOrDeclareRegister(var, type);
expr.buildInto(rhs, target);
@@ -97,8 +144,7 @@ public class StatementBuilder {
ctx.getScope().setConstValue(var, constVal);
else
ctx.getScope().clearConstValue(var);
- } catch (Throwable ignored) {
- }
+ } catch (Throwable ignored) {}
ctx.clearVarType();
return;
@@ -163,11 +209,9 @@ public class StatementBuilder {
ctx.setVarType(decl.getType());
// 2. 为当前声明的变量分配一个全新的虚拟寄存器
- // 这样可以保证该变量和初始值表达式中的变量物理上独立,不会发生别名/串扰
IRVirtualRegister dest = ctx.newRegister();
// 3. 将初始值表达式的计算结果写入新分配的寄存器
- // 即使初始值是某个已存在变量(如 outer_i),这里是值的拷贝
expr.buildInto(decl.getInitializer().get(), dest);
// 声明赋初值时登记常量
@@ -177,8 +221,7 @@ public class StatementBuilder {
ctx.getScope().setConstValue(decl.getName(), constVal);
else
ctx.getScope().clearConstValue(decl.getName());
- } catch (Throwable ignored) {
- }
+ } catch (Throwable ignored) {}
ctx.clearVarType();
ctx.getScope().declare(decl.getName(), decl.getType(), dest);
@@ -188,18 +231,18 @@ public class StatementBuilder {
}
return;
}
+
if (stmt instanceof ReturnNode ret) {
// return 语句
if (ret.getExpression().isPresent()) {
- // return 带返回值
IRVirtualRegister r = expr.build(ret.getExpression().get());
InstructionFactory.ret(ctx, r);
} else {
- // return 无返回值
InstructionFactory.retVoid(ctx);
}
return;
}
+
if (stmt instanceof BreakNode) {
// break 语句:跳转到当前最近一层循环的结束标签
if (breakTargets.isEmpty()) {
@@ -208,6 +251,7 @@ public class StatementBuilder {
InstructionFactory.jmp(ctx, breakTargets.peek());
return;
}
+
if (stmt instanceof ContinueNode) {
// continue 语句:跳转到当前最近一层循环的 step 起始标签
if (continueTargets.isEmpty()) {
@@ -216,10 +260,12 @@ public class StatementBuilder {
InstructionFactory.jmp(ctx, continueTargets.peek());
return;
}
+
// 不支持的语句类型
throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt);
}
+
/**
* 获取变量名对应的寄存器,如果尚未声明则新声明一个并返回。
*
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/value/IRConstant.java b/src/main/java/org/jcnc/snow/compiler/ir/value/IRConstant.java
index 776d688c366b847d99ee91a18862a950fa988e43..8515bb7c0d42ac61d3081f42a750d6da7a914e2f 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/value/IRConstant.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/value/IRConstant.java
@@ -3,29 +3,99 @@ package org.jcnc.snow.compiler.ir.value;
import org.jcnc.snow.compiler.ir.core.IRValue;
/**
- * IRConstant —— 表示中间表示(IR)系统中的常量值。
+ * {@code IRConstant} —— 表示中间表示(IR)系统中的常量值节点。
*
- * 常量用于表示在编译期间已知的不可变值,例如字面整数、浮点数、布尔值或字符串。
- * 与 {@link IRVirtualRegister} 不同,常量不需要通过寄存器存储,
- * 可直接作为 IR 指令的操作数使用。
- *
- * 典型应用:
- * - 加载常量指令: v1 = CONST 42
- * - 计算表达式: v2 = ADD v1, 100
+ * 常量用于表示在编译期间已知的不可变值,例如字面整数、浮点数、布尔值或字符串等。
+ * 与 {@link IRVirtualRegister} 不同,IRConstant 不需要通过寄存器存储,
+ * 可直接作为 IR 指令的操作数(立即数/常量池引用等)。
*/
public record IRConstant(Object value) implements IRValue {
/**
- * 将常量值转换为字符串,用于打印 IR 指令或调试输出。
+ * 通用工厂方法:将任意 Java 对象包装为 IRConstant。
+ *
+ * @param v 任意对象
+ * @return IRConstant 封装后的常量
+ */
+ public static IRConstant fromObject(Object v) {
+ return new IRConstant(v);
+ }
+
+ /**
+ * 数字字面量工厂:接受源码字面量字符串,解析为 int/long/double 等 Java 数值类型。
*
- * 例如:
- * - 整数常量: 42
- * - 字符串常量: "hello"
+ * 支持下划线分隔(如 "1_000_000")、类型后缀(如 'l','d','f' 等),并自动转换为
+ *
+ * double:带小数点或科学计数法
+ * long/byte/short/int:依类型后缀或值范围判定
+ * 解析失败时兜底为字符串原文,保证编译不中断
+ *
+ *
+ * @param literal 字面量字符串(可能含类型后缀、下划线分隔)
+ * @return IRConstant 包装的数字常量
+ */
+ public static IRConstant fromNumber(String literal) {
+ if (literal == null) return new IRConstant(null);
+ String s = literal.replace("_", ""); // 去除下划线分隔符
+ // 检查并处理类型后缀(b/s/l/f/d)
+ char last = Character.toLowerCase(s.charAt(s.length() - 1));
+ String core = switch (last) {
+ case 'b', 's', 'l', 'f', 'd' -> s.substring(0, s.length() - 1);
+ default -> s;
+ };
+ try {
+ // 浮点:含小数点或科学计数法
+ if (core.contains(".") || core.contains("e") || core.contains("E")) {
+ return new IRConstant(Double.parseDouble(core));
+ }
+ long lv = Long.parseLong(core); // 整型
+ return switch (last) {
+ case 'b' -> new IRConstant((byte) lv); // 字节型
+ case 's' -> new IRConstant((short) lv); // 短整型
+ case 'l' -> new IRConstant(lv); // 长整型
+ default -> new IRConstant((int) lv); // 默认 int
+ };
+ } catch (NumberFormatException e) {
+ // 解析失败时,回退为原始字符串,避免编译中断
+ return new IRConstant(core);
+ }
+ }
+
+ /**
+ * 字符串字面量工厂。
+ *
+ * @param s 字符串内容
+ * @return IRConstant 封装的字符串常量
+ */
+ public static IRConstant fromString(String s) {
+ return new IRConstant(s);
+ }
+
+ /**
+ * 布尔字面量工厂。
*
- * @return 常量的字符串表示
+ * @param v 布尔值
+ * @return IRConstant 封装的布尔常量
+ */
+ public static IRConstant fromBoolean(boolean v) {
+ return new IRConstant(v);
+ }
+
+ /**
+ * 调试友好的字符串表示。
+ *
+ * 字符串常量输出带引号,转义引号和反斜杠
+ * null 输出为 "null"
+ * 其它类型调用 toString()
+ *
*/
@Override
public String toString() {
- return value.toString();
+ if (value == null) return "null";
+ if (value instanceof String s) {
+ String escaped = s.replace("\\", "\\\\").replace("\"", "\\\"");
+ return "\"" + escaped + "\"";
+ }
+ return String.valueOf(value);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
index 56cb63db8daa8aa5246548d20a56a5e10ec634e5..6b5c3ba533495501cd5b3aad61e2f4490a9b3ff9 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/core/LexerEngine.java
@@ -1,9 +1,11 @@
package org.jcnc.snow.compiler.lexer.core;
+import org.jcnc.snow.common.SnowConfig;
import org.jcnc.snow.compiler.lexer.base.TokenScanner;
import org.jcnc.snow.compiler.lexer.scanners.*;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
+import org.jcnc.snow.compiler.lexer.utils.TokenPrinter;
import java.io.File;
import java.util.ArrayList;
@@ -54,6 +56,7 @@ public class LexerEngine {
/* 2. 后置整体校验 */
validateTokens();
/* 3. 打印 token */
+ // TODO: TokenPrinter
// if (SnowConfig.isDebug()) {
// TokenPrinter.print(tokens);
// }
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
index 01713b38c8420e50ecc0d57d5dd0549cdbe64744..d1893c46d58d5014dc7d04e10f5a396654ace691 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
@@ -28,7 +28,9 @@ public class TokenFactory {
private static final Set KEYWORDS = Set.of
("module", "function", "params", "returns", "body", "end",
"if", "then", "else", "loop", "declare", "return", "import", "init",
- "cond", "step", "globals", "break", "continue", "const");
+ "cond", "step", "globals", "break", "continue", "const",
+ "new", "extends"
+ );
/**
* 内置类型名称集合,如 int、string 等。
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java
index 2a1b3712b58c286c6e7d5e07de736c10a2a958ea..836c547be8f4fadac2949202b1552347fd61f4b3 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ModuleNode.java
@@ -7,44 +7,62 @@ import java.util.List;
import java.util.StringJoiner;
/**
- * 表示模块定义的 AST 节点。
- * 一个模块通常由模块名、导入语句列表和函数定义列表组成。
+ * {@code ModuleNode}
+ *
+ * 抽象语法树(AST)顶层节点 —— 模块定义。
+ *
+ * 代表一个完整的源码模块/文件(如 main, math 等)。
+ * 包含模块名、导入列表、全局变量、结构体和函数定义等全部模块级内容。
+ * 是整个编译流程的入口节点。
+ *
+ *
*
- * @param name 模块名称。
- * @param imports 模块导入列表,每个导入是一个 {@link ImportNode}。
- * @param functions 模块中的函数列表,每个函数是一个 {@link FunctionNode}。
- * @param context 节点上下文信息(包含行号、列号等)
+ * @param name 模块名称
+ * @param imports 导入模块列表
+ * @param globals 全局变量/常量声明
+ * @param structs 结构体定义列表
+ * @param functions 函数定义列表
+ * @param context 源代码位置信息
*/
public record ModuleNode(
String name,
List imports,
List globals,
+ List structs,
List functions,
NodeContext context
) implements Node {
/**
- * 返回模块节点的字符串表示形式,包含模块名、导入模块列表和函数列表。
+ * 返回模块节点的简要字符串表示(用于日志、调试)。
+ * 列出模块名、导入、全局变量、结构体、函数等简明内容。
*
- * @return 模块的简洁字符串表示,用于调试和日志输出。
+ * @return 字符串形式,如
+ * Module(name=main, imports=[math], globals=[int x], structs=[Foo], functions=[bar])
*/
@Override
public String toString() {
- StringJoiner importJoiner = new StringJoiner(", ");
- for (ImportNode imp : imports) {
- importJoiner.add(imp.moduleName());
- }
- StringJoiner globalJoiner = new StringJoiner(", ");
- for (DeclarationNode g : globals) {
- globalJoiner.add(g.getType() + " " + g.getName());
- }
- StringJoiner funcJoiner = new StringJoiner(", ");
- for (FunctionNode fn : functions) {
- funcJoiner.add(fn.name());
- }
- return "Module(name=" + name
- + ", imports=[" + importJoiner + "]"
- + ", globals=[" + globalJoiner + "]"
- + ", functions=[" + funcJoiner + "])";
+ // 1) 导入模块列表字符串
+ StringJoiner impJ = new StringJoiner(", ");
+ imports.forEach(i -> impJ.add(i.moduleName()));
+
+ // 2) 全局变量/常量列表字符串
+ StringJoiner globJ = new StringJoiner(", ");
+ globals.forEach(g -> globJ.add(g.getType() + " " + g.getName()));
+
+ // 3) 结构体名列表字符串
+ StringJoiner structJ = new StringJoiner(", ");
+ structs.forEach(s -> structJ.add(s.name()));
+
+ // 4) 函数名列表字符串
+ StringJoiner funcJ = new StringJoiner(", ");
+ functions.forEach(f -> funcJ.add(f.name()));
+
+ // 5) 综合输出
+ return "Module(name=" + name +
+ ", imports=[" + impJ + "]" +
+ ", globals=[" + globJ + "]" +
+ ", structs=[" + structJ + "]" +
+ ", functions=[" + funcJ + "])";
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/NewExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/NewExpressionNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6cbdde78be6d2d6932e41204d667f8adffbe2e8
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/NewExpressionNode.java
@@ -0,0 +1,50 @@
+package org.jcnc.snow.compiler.parser.ast;
+
+import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
+import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
+
+import java.util.List;
+
+/**
+ * {@code NewExpressionNode}
+ *
+ * 抽象语法树(AST)节点 —— 对象创建表达式。
+ *
new TypeName(arg1, arg2, ...)
+ *
+ *
+ * 语义:在源码中通过 new 关键字实例化某个类型,
+ * 实参列表为构造函数参数。表达式类型为对应结构体类型。
+ *
+ *
+ *
+ * 字段说明:
+ *
+ * {@code typeName} —— 创建对象的目标类型名(如 "Person"、"MyStruct")。
+ * {@code arguments} —— 构造函数参数表达式列表(可为空)。
+ * {@code ctx} —— 源码位置上下文(行列信息,便于报错和调试定位)。
+ *
+ *
+ *
+ * @param typeName 目标类型名
+ * @param arguments 构造参数表达式列表
+ * @param ctx 源代码位置信息
+ */
+public record NewExpressionNode(
+ String typeName,
+ List arguments,
+ NodeContext ctx
+) implements ExpressionNode {
+
+ /**
+ * 获取本节点的源代码位置信息(行号、列号等)。
+ *
+ * 实现 ExpressionNode 接口的抽象方法,便于统一错误处理与调试。
+ *
+ *
+ * @return 当前节点的源代码上下文信息
+ */
+ @Override
+ public NodeContext context() {
+ return ctx;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/StructNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/StructNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..b55815758b0ac6db1a5a4b0bcf9c19b26d0ebf05
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/StructNode.java
@@ -0,0 +1,52 @@
+package org.jcnc.snow.compiler.parser.ast;
+
+import org.jcnc.snow.compiler.parser.ast.base.Node;
+import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
+
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * {@code StructNode}
+ *
+ * AST 节点 —— 结构体定义(struct)。
+ *
+ *
+ *
+ * 描述一个结构体类型,包括字段、可选构造函数、方法列表等。
+ * 结构体可声明零个或多个字段,可选构造函数(init),
+ * 以及零个或多个方法。
+ *
+ * @param name 结构体名称
+ * @param parent 父类名称(无继承时为 {@code null})
+ * @param fields 字段声明列表
+ * @param inits 构造函数(可为 null)
+ * @param methods 方法列表
+ * @param context 源码位置信息
+ */
+public record StructNode(
+ String name,
+ String parent,
+ List fields,
+ List inits,
+ List methods,
+ NodeContext context
+) implements Node {
+
+ /**
+ * 输出结构体节点的简明字符串表示,便于调试和日志查看。
+ *
+ * @return 结构体节点的简要信息,包括名称、字段、构造函数、方法
+ */
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", "StructNode[", "]")
+ .add("name=" + name)
+ .add("parent=" + parent)
+ .add("fields=" + fields.size())
+ .add("ctors=" + (inits == null ? 0 : inits.size()))
+ .add("methods=" + methods.size())
+ .toString();
+ }
+
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/base/ModuleNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/ModuleNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae7b9ad536ec28a03098054f81398314c028d070
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/base/ModuleNode.java
@@ -0,0 +1,81 @@
+package org.jcnc.snow.compiler.parser.ast.base;
+
+import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.ast.ImportNode;
+import org.jcnc.snow.compiler.parser.ast.StructNode;
+
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * {@code ModuleNode} 表示一个源文件或逻辑模块的 AST 根节点。
+ *
+ *
+ * 用于描述一个完整模块的全部语法内容,包括模块名、导入依赖、全局变量、结构体定义、函数定义等。
+ * 作为语法树的顶层节点,供后续语义分析与 IR 生成统一处理。
+ *
+ *
+ * 字段说明:
+ *
+ * {@code name} —— 模块名称,通常对应文件名或逻辑包名,要求唯一。
+ * {@code imports} —— import 导入列表,描述本模块依赖的其它模块名。
+ * {@code globals} —— 全局变量与常量声明列表,作用域为整个模块。
+ * {@code structs} —— 结构体定义列表(支持零个或多个结构体)。
+ * {@code functions} —— 函数定义列表(支持零个或多个函数)。
+ * {@code context} —— 源代码位置信息,用于报错定位与调试。
+ *
+ *
+ *
+ * 该类为 Java record,天然不可变。构造参数即为字段名,调用时自动生成 getter 方法。
+ *
+ *
+ * @param name 模块名称(如 "main")
+ * @param imports 导入依赖列表(ImportNode)
+ * @param globals 全局变量声明列表(DeclarationNode)
+ * @param structs 结构体定义列表(StructNode)
+ * @param functions 函数定义列表(FunctionNode)
+ * @param context 源代码位置信息(NodeContext)
+ */
+public record ModuleNode(
+ String name,
+ List imports,
+ List globals,
+ List structs,
+ List functions,
+ NodeContext context
+) implements Node {
+
+ /**
+ * 重写 toString 方法,便于调试和日志输出。
+ * 展示模块名、导入、全局变量、结构体、函数的简要列表。
+ *
+ * @return 字符串形式,格式如:
+ * Module(name=main, imports=[foo, bar], globals=[int x, ...], structs=[A, B], functions=[f, g])
+ */
+ @Override
+ public String toString() {
+ // 构造 import 字符串
+ StringJoiner impJ = new StringJoiner(", ");
+ imports.forEach(i -> impJ.add(i.moduleName()));
+
+ // 构造全局变量声明字符串(类型+名称)
+ StringJoiner globJ = new StringJoiner(", ");
+ globals.forEach(g -> globJ.add(g.getType() + " " + g.getName()));
+
+ // 构造结构体名列表
+ StringJoiner structJ = new StringJoiner(", ");
+ structs.forEach(s -> structJ.add(s.name()));
+
+ // 构造函数名列表
+ StringJoiner funcJ = new StringJoiner(", ");
+ functions.forEach(f -> funcJ.add(f.name()));
+
+ // 综合输出
+ return "Module(name=" + name +
+ ", imports=[" + impJ + "]" +
+ ", globals=[" + globJ + "]" +
+ ", structs=[" + structJ + "]" +
+ ", functions=[" + funcJ + "])";
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/NewObjectParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/NewObjectParselet.java
new file mode 100644
index 0000000000000000000000000000000000000000..fbda9e4ca6ef1f47fdaa023293749fca70e6d0ea
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/NewObjectParselet.java
@@ -0,0 +1,84 @@
+package org.jcnc.snow.compiler.parser.expression;
+
+import org.jcnc.snow.compiler.lexer.token.Token;
+import org.jcnc.snow.compiler.lexer.token.TokenType;
+import org.jcnc.snow.compiler.parser.ast.NewExpressionNode;
+import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
+import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
+import org.jcnc.snow.compiler.parser.context.ParserContext;
+import org.jcnc.snow.compiler.parser.context.TokenStream;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
+import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@code NewObjectParselet}
+ *
+ * 解析对象创建表达式 new TypeName(arg1, arg2, ...)
的前缀解析器实现。
+ *
+ * 用于 Pratt 解析框架,实现 new 关键字前缀的自定义解析逻辑。
+ * 支持类型名为内置类型(TokenType.TYPE)或用户结构体名(TokenType.IDENTIFIER)。
+ * 支持可变参数列表,参数为任意表达式。
+ *
+ *
+ */
+public class NewObjectParselet implements PrefixParselet {
+
+ /**
+ * 解析 new 对象创建表达式,并生成 AST 新建节点。
+ *
+ * @param ctx 语法分析上下文,包含 TokenStream 及源文件名等信息
+ * @param token 当前读取到的 new 关键字 Token
+ * @return 表达式节点(NewExpressionNode)
+ *
+ *
+ * 解析流程:
+ *
+ * 读取类型名(支持内建类型或结构体名);
+ * 读取参数列表,参数为表达式,用逗号分隔;
+ * 封装为 NewExpressionNode,记录源码位置信息。
+ *
+ * 出错时会抛出 UnexpectedToken 异常。
+ *
+ */
+ @Override
+ public ExpressionNode parse(ParserContext ctx, Token token) {
+ TokenStream ts = ctx.getTokens();
+
+ // ==========================
+ // 1) 解析类型名
+ // ==========================
+ // 类型名只能为内建类型(TYPE)或用户结构体名(IDENTIFIER)
+ if (ts.peek().getType() != TokenType.TYPE && ts.peek().getType() != TokenType.IDENTIFIER) {
+ var t = ts.peek();
+ throw new UnexpectedToken(
+ "期望的标记类型为 TYPE 或 IDENTIFIER,但实际得到的是 " +
+ t.getType() + " ('" + t.getLexeme() + "')",
+ t.getLine(), t.getCol()
+ );
+ }
+ String typeName = ts.next().getLexeme();
+
+ // ==========================
+ // 2) 解析构造参数列表
+ // ==========================
+ ts.expect("("); // 必须有左括号
+
+ List args = new ArrayList<>();
+ if (!ts.match(")")) { // 非空参数列表
+ // 连续处理逗号分隔的多个参数
+ do {
+ args.add(new PrattExpressionParser().parse(ctx));
+ } while (ts.match(","));
+ ts.expect(")"); // 结尾必须为右括号
+ }
+
+ // ==========================
+ // 3) 封装为 AST 节点并返回
+ // ==========================
+ NodeContext nc = new NodeContext(token.getLine(), token.getCol(), ctx.getSourceName());
+ return new NewExpressionNode(typeName, args, nc);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index f18813f3ef1497d20d79cc51dbc347f0904100ac..3fa483c1639638079c182a51d9b5b5e613919da9 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -13,62 +13,62 @@ import java.util.HashMap;
import java.util.Map;
/**
- * {@code PrattExpressionParser} 基于 Pratt 算法的表达式解析器实现。
+ * {@code PrattExpressionParser}
*
- * 该类通过前缀(PrefixParselet)和中缀(InfixParselet)解析器注册表,
- * 支持灵活扩展的表达式语法,包括字面量、变量、函数调用、成员访问和各种运算符表达式。
- *
- *
- * 运算符优先级通过枚举控制,结合递归解析实现高效的优先级处理和语法结构解析。
- * 未注册的语法类型或运算符会统一抛出 {@link UnsupportedFeature} 异常。
+ * 基于 Pratt 算法的表达式解析器(经典“运算符优先级”递归解析框架)。
+ *
+ * 支持注册前缀和中缀解析器,灵活组合表达式语法。
+ * 支持字面量、变量、函数调用、成员访问、对象创建、各类一元/二元/多元运算。
+ * 可快速扩展新语法(只需注册新的 Parselet 即可)。
+ * 出错时统一抛出 {@link UnsupportedFeature},便于调试和错误提示。
+ *
*
*/
public class PrattExpressionParser implements ExpressionParser {
/**
- * 前缀解析器注册表(按 Token 类型名或词素索引)。
- *
- * 用于存储所有支持的前缀表达式解析器,例如字面量、变量、分组、数组、一元运算等。
- * 支持通过 TokenType 的名称和特定词素(如 "(", "[")两种方式索引。
- *
+ * 前缀解析器注册表(通过 Token 类型名或词素作为索引)。
+ *
+ * 如数字字面量、标识符、字符串、布尔值、new、分组、数组、一元运算等。
+ * 支持同时用 TokenType 名称和具体词素(如 "("、"-")注册。
+ *
*/
private static final Map prefixes = new HashMap<>();
/**
- * 中缀解析器注册表(按运算符词素索引)。
- *
- * 用于存储所有支持的中缀表达式解析器,如二元运算、函数调用、下标、成员访问等。
- * 仅通过词素索引(如 "+", "-", "(", "[" 等)。
- *
+ * 中缀解析器注册表(通过运算符词素索引)。
+ *
+ * 如 + - * / % 及比较、逻辑、函数调用、下标、成员访问等。
+ * 仅词素索引(如 "+"、"-"、"("、".")
+ *
*/
private static final Map infixes = new HashMap<>();
static {
- // -------- 前缀解析器注册 --------
- // 注册数字字面量解析
- prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
- // 注册标识符(变量名)解析
- prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
- // 注册字符串字面量解析
- prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
- // 注册布尔字面量解析
- prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
-
- // 支持括号分组、数组字面量
- prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
- prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
- // 兼容直接以词素注册(如 '(' '[')
+ // ----------------- 前缀解析器注册 -----------------
+ // 各种字面量/标识符
+ prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
+ prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
+ prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
+ prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
+
+ // 分组与数组字面量(两种索引方式)
+ prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
+ prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
prefixes.put("(", new GroupingParselet());
prefixes.put("[", new ArrayLiteralParselet());
- // 一元前缀运算符(负号、逻辑非)
+ // 一元前缀运算符(如负号、逻辑非),同样用两种方式注册
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
prefixes.put("-", new UnaryOperatorParselet());
prefixes.put("!", new UnaryOperatorParselet());
- // -------- 中缀解析器注册 --------
- // 注册常见二元运算符(加减乘除、取模)
+ // 对象创建 new TypeName(args...)
+ prefixes.put("new", new NewObjectParselet());
+
+ // ----------------- 中缀解析器注册 -----------------
+ // 常见二元算数运算符
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
@@ -79,25 +79,25 @@ public class PrattExpressionParser implements ExpressionParser {
infixes.put("<", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put(">=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put("<=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
- // 相等性
+ // 相等性判断
infixes.put("==", new BinaryOperatorParselet(Precedence.EQUALITY, true));
infixes.put("!=", new BinaryOperatorParselet(Precedence.EQUALITY, true));
- // 逻辑与或
+ // 逻辑运算
infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true));
- infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
- // 调用、索引、成员访问
- infixes.put("(", new CallParselet());
- infixes.put("[", new IndexParselet());
- infixes.put(".", new MemberParselet());
+ infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
+ // 函数调用、数组下标、成员访问
+ infixes.put("(", new CallParselet());
+ infixes.put("[", new IndexParselet());
+ infixes.put(".", new MemberParselet());
}
/**
- * 解析任意表达式的统一入口。
+ * 统一表达式解析入口,自动以最低优先级递归解析整个表达式。
*
- * 该方法将以最低优先级启动表达式递归解析,能够自动适配和处理多层嵌套或复杂组合表达式。
+ * 能解析嵌套、复合等所有合法表达式结构。
*
*
- * @param ctx 当前解析上下文对象(持有 token 流等信息)
+ * @param ctx 当前解析上下文对象(含 token 流等信息)
* @return 解析得到的表达式 AST 节点对象
*/
@Override
@@ -106,50 +106,42 @@ public class PrattExpressionParser implements ExpressionParser {
}
/**
- * 按指定优先级解析表达式(Pratt 算法核心)。
- *
- * 1. 先取当前 token,查找对应的前缀解析器进行初始解析,构建表达式左侧(如字面量、变量等)。
- * 2. 然后循环检测是否有更高优先级的中缀操作符,
- * 若有则递归处理右侧表达式并组合为新的表达式节点。
- *
+ * Pratt 算法主递归循环:按给定优先级递归解析表达式。
*
- * 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常。
+ * 实现按优先级吸收中缀操作符(如连续算术、链式调用、组合表达式等)。
*
*
* @param ctx 解析上下文
- * @param prec 当前运算符优先级(用于控制递归层级)
- * @return 解析构建好的表达式节点
- * @throws UnsupportedFeature 遇到未注册的解析器时抛出
+ * @param prec 当前已绑定优先级
+ * @return 已解析的表达式节点
*/
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
- // 取下一个 token 作为本轮前缀表达式起始
+ // 1) 消耗一个 token 作为前缀起点
Token token = ctx.getTokens().next();
- // 查找前缀解析器(先按类型名,再按词素)
- PrefixParselet prefix = prefixes.get(token.getType().name());
+ // 2) 查找前缀解析器(优先按词素,再按 TokenType)
+ PrefixParselet prefix = prefixes.get(token.getLexeme());
if (prefix == null) {
- prefix = prefixes.get(token.getLexeme());
+ prefix = prefixes.get(token.getType().name());
}
if (prefix == null) {
- // 未找到前缀解析器则报错
+ // 未注册前缀解析器,直接报错
throw new UnsupportedFeature(
- "没有为该 Token 类型注册前缀解析器: " + token.getType(),
+ "没有为该 Token 注册前缀解析器: " + token.getLexeme() + " / " + token.getType(),
token.getLine(),
token.getCol()
);
}
- // 执行前缀解析,获得左侧表达式
+ // 3) 前缀解析得到左侧表达式
ExpressionNode left = prefix.parse(ctx, token);
- // 不断尝试查找优先级更高的中缀运算符,递归处理表达式链
- while (!ctx.getTokens().isAtEnd()
- && prec.ordinal() < nextPrecedence(ctx)) {
- // 查看下一个 token 词素,查找中缀解析器
+ // 4) 主循环:不断吸收更高优先级的中缀操作,直到优先级不再提升
+ while (!ctx.getTokens().isAtEnd() && prec.ordinal() < nextPrecedence(ctx)) {
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
if (infix == null) {
- // 若未注册中缀解析器,则直接抛异常(常见于语法错误)
+ // nextPrecedence > prec 时一般已注册中缀解析器
Token t = ctx.getTokens().peek();
throw new UnsupportedFeature(
"没有为该运算符注册中缀解析器: '" + lex + "'",
@@ -157,21 +149,21 @@ public class PrattExpressionParser implements ExpressionParser {
t.getCol()
);
}
- // 使用中缀解析器处理表达式组合
+ // 递归组合更高优先级的中缀表达式
left = infix.parse(ctx, left);
}
- // 返回本层递归已解析的表达式节点
+ // 5) 返回本层解析完成的表达式节点
return left;
}
/**
- * 获取下一个 token 词素对应的中缀运算符优先级(Pratt 算法关键)。
+ * 获取下一个 token 对应的中缀运算符优先级(Pratt 算法关键)。
*
- * 用于决定当前是否需要递归处理更高优先级的中缀操作。
+ * 若无注册的中缀解析器,则返回 -1。
*
*
- * @param ctx 当前解析上下文
- * @return 下一个中缀运算符的优先级序号;若无注册解析器则返回 -1
+ * @param ctx 解析上下文
+ * @return 下一个运算符优先级序号(无则-1)
*/
private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
index ae456740dcee910590699939b32122c7f2fdcd9c..019238943cb2e4e8d80c9d005b84fc94f53d6f25 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
@@ -62,10 +62,38 @@ public class ASTPrinter {
for (ImportNode imp : m.imports()) {
System.out.println(pad + " import " + imp.moduleName());
}
+ for (StructNode s : m.structs()) {
+ print(s, indent + 1);
+ }
for (FunctionNode fn : m.functions()) {
print(fn, indent + 1);
}
}
+
+ case StructNode s -> {
+ System.out.print(pad + "struct " + s.name());
+ if (s.parent() != null && !s.parent().isEmpty()) {
+ System.out.print(" extends " + s.parent());
+ }
+ System.out.println();
+ for (DeclarationNode f : s.fields()) {
+ print(f, indent + 1);
+ }
+ // 打印所有构造函数 inits
+ if (s.inits() != null && !s.inits().isEmpty()) {
+ for (FunctionNode ctor : s.inits()) {
+ System.out.println(pad + " [init]");
+ print(ctor, indent + 2);
+ }
+ }
+ // 打印所有方法
+ if (s.methods() != null && !s.methods().isEmpty()) {
+ for (FunctionNode m : s.methods()) {
+ print(m, indent + 1);
+ }
+ }
+ }
+
case FunctionNode(
String name, List parameters, String returnType, List body,
NodeContext _
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java
index dba62b3de07fdcfd1670477b33a150c19a74cd94..8d155e9b41d70e5fcfd4d6f285a9796c8f35fcac 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java
@@ -2,15 +2,16 @@ package org.jcnc.snow.compiler.parser.function;
import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
-import org.jcnc.snow.compiler.parser.ast.ReturnNode;
-import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
-import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ParameterNode;
+import org.jcnc.snow.compiler.parser.ast.ReturnNode;
+import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
+import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.context.ParseException;
import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.context.TokenStream;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser;
import org.jcnc.snow.compiler.parser.utils.FlexibleSectionParser.SectionDefinition;
@@ -22,15 +23,13 @@ import java.util.*;
* 实现 {@link TopLevelParser} 接口,用于将源代码中的函数块解析为抽象语法树(AST)中的 {@link FunctionNode}。
*
*
- * 本类使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分:
- *
- *
+ * 使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分:
*
- * 函数头(关键字 {@code function:} 与函数名)
- * 参数列表(params 区块)
- * 返回类型(returns 区块)
- * 函数体(body 区块)
- * 函数结束(关键字 {@code end function})
+ * 函数头(关键字 {@code function:} 与函数名)
+ * 参数列表(params 区块)
+ * 返回类型(returns 区块)
+ * 函数体(body 区块)
+ * 函数结束(关键字 {@code end function})
*
*
*
@@ -47,76 +46,80 @@ public class FunctionParser implements TopLevelParser {
* 顶层语法解析入口。
*
*
- * 该方法负责完整解析函数定义,包括其所有组成部分,并构建对应的 {@link FunctionNode}。
+ * 解析并生成函数的 AST。
+ * 该方法从源代码中获取 {@link TokenStream},并按照函数定义的不同区块解析,最终生成 {@link FunctionNode}。
*
*
- * @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等作用域信息。
- * @return 构建完成的 {@link FunctionNode} 抽象语法树节点。
+ * @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等信息
+ * @return 构建完成的 {@link FunctionNode} 对象
*/
@Override
public FunctionNode parse(ParserContext ctx) {
- // 获取当前解析上下文中的 token 流
TokenStream ts = ctx.getTokens();
- // 记录当前 token 的行号、列号和源文件名,用于错误报告或生成节点上下文
- int line = ctx.getTokens().peek().getLine();
- int column = ctx.getTokens().peek().getCol();
+ int line = ts.peek().getLine();
+ int column = ts.peek().getCol();
String file = ctx.getSourceName();
- // 解析函数头(例如可能是 `function` 关键字等)
+ // 解析函数头 (function:)
parseFunctionHeader(ts);
- // 解析函数名
+ // 函数名
String functionName = parseFunctionName(ts);
- // 用于存放解析出来的参数列表
List parameters = new ArrayList<>();
- // 用于存放返回类型,这里用数组是为了在闭包中修改其值
- String[] returnType = new String[1];
- // 用于存放函数体语句列表
+ String[] returnType = new String[1]; // 使用数组模拟可变引用
List body = new ArrayList<>();
- // 获取函数可以包含的可选节(如参数、返回类型、主体等)的定义映射
+ // 定义函数可选区块规则 (params, returns, body)
Map sections = getSectionDefinitions(parameters, returnType, body);
- // 调用通用的多节解析器,实际根据 sections 中注册的规则解析各部分内容
+ // 解析这些区块
FlexibleSectionParser.parse(ctx, ts, sections);
- // 如果函数体为空且返回类型为 void,自动补充一个空的 return 语句
- if (body.isEmpty() && returnType[0].equals("void")) {
+ // 如果函数体为空且返回 void,补充一个空 return
+ if (body.isEmpty() && "void".equals(returnType[0])) {
body.add(new ReturnNode(null, new NodeContext(line, column, file)));
}
- // 检查参数名称是否重复
- Set set = new HashSet<>();
- parameters.forEach((node) -> {
- final String name = node.name();
- if (set.contains(name)) {
- // 如果参数名重复,抛出带具体行列信息的解析异常
- throw new ParseException(String.format("参数 `%s` 重定义", name),
- node.context().line(), node.context().column());
+ // 检查参数名是否重复
+ Set seen = new HashSet<>();
+ for (ParameterNode node : parameters) {
+ if (!seen.add(node.name())) {
+ throw new ParseException(
+ String.format("参数 `%s` 重定义", node.name()),
+ node.context().line(),
+ node.context().column());
}
- set.add(name);
- });
+ }
- // 解析函数的尾部(例如右大括号或者 end 标志)
+ // 解析函数结尾
parseFunctionFooter(ts);
- // 返回完整的函数节点,包含函数名、参数、返回类型、函数体以及源位置信息
- return new FunctionNode(functionName, parameters, returnType[0], body, new NodeContext(line, column, file));
+ return new FunctionNode(functionName, parameters, returnType[0], body,
+ new NodeContext(line, column, file));
}
+ /* ====================================================================== */
+ /* -------------------------- Section Definitions ----------------------- */
+ /* ====================================================================== */
+
/**
- * 构造函数定义中各区块的解析规则(params、returns、body)。
+ * 定义函数的各个语义区块及其解析规则,包括参数列表、返回类型及函数体。
*
*
- * 每个 {@link SectionDefinition} 包含两个部分: 区块起始判断器(基于关键字)与具体的解析逻辑。
+ * 此方法将定义如下的三个区块:
+ *
+ * "params" 区块,解析参数列表。
+ * "returns" 区块,解析返回类型。
+ * "body" 区块,解析函数体。
+ *
*
*
- * @param params 参数节点收集容器,解析结果将存入此列表。
- * @param returnType 返回类型容器,以单元素数组方式模拟引用传递。
- * @param body 函数体语句节点列表容器。
- * @return 区块关键字到解析定义的映射表。
+ * @param params 存储函数参数列表的集合
+ * @param returnType 存储函数返回类型的字符串数组
+ * @param body 存储函数体的语句列表
+ * @return 各个区块的解析定义
*/
private Map getSectionDefinitions(
List params,
@@ -142,8 +145,12 @@ public class FunctionParser implements TopLevelParser {
return map;
}
+ /* ====================================================================== */
+ /* ---------------------------- 函数头/尾 ------------------------------- */
+ /* ====================================================================== */
+
/**
- * 解析函数头部标识符 {@code function:},并跳过其后多余注释与空行。
+ * 解析函数头部分,期望的格式为 `function:`。
*
* @param ts 当前使用的 {@link TokenStream}。
*/
@@ -158,7 +165,7 @@ public class FunctionParser implements TopLevelParser {
* 解析函数名称(标识符)并跳过换行。
*
* @param ts 当前使用的 {@link TokenStream}。
- * @return 函数名字符串。
+ * @return 函数名称字符串。
*/
private String parseFunctionName(TokenStream ts) {
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
@@ -167,7 +174,7 @@ public class FunctionParser implements TopLevelParser {
}
/**
- * 解析函数结束标记 {@code end function}。
+ * 解析函数结束标记 `end function`。
*
* @param ts 当前使用的 {@link TokenStream}。
*/
@@ -177,18 +184,14 @@ public class FunctionParser implements TopLevelParser {
}
/**
- * 解析函数参数列表。
- *
- *
- * 支持声明后附加注释,格式示例:
+ * 解析函数参数列表,支持附加注释,格式为:
*
* params:
* declare x: int // 说明文字
* declare y: float
*
- *
*
- * @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等作用域信息。
+ * @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等信息。
* @return 所有参数节点的列表。
*/
private List parseParameters(ParserContext ctx) {
@@ -208,19 +211,28 @@ public class FunctionParser implements TopLevelParser {
continue;
}
String lex = ts.peek().getLexeme();
- if (lex.equals("returns") || lex.equals("body") || lex.equals("end")) {
- break;
- }
+ if (lex.equals("returns") || lex.equals("body") || lex.equals("end")) break;
- // 获取当前 token 的行号、列号和文件名
- int line = ctx.getTokens().peek().getLine();
- int column = ctx.getTokens().peek().getCol();
+ int line = ts.peek().getLine();
+ int column = ts.peek().getCol();
String file = ctx.getSourceName();
ts.expect("declare");
String pname = ts.expectType(TokenType.IDENTIFIER).getLexeme();
ts.expect(":");
- String ptype = ts.expectType(TokenType.TYPE).getLexeme();
+
+ // 既接受 TYPE 也接受 IDENTIFIER
+ String ptype;
+ if (ts.peek().getType() == TokenType.TYPE || ts.peek().getType() == TokenType.IDENTIFIER) {
+ ptype = ts.next().getLexeme();
+ } else {
+ var t = ts.peek();
+ throw new UnexpectedToken(
+ "期望 TYPE 或 IDENTIFIER,但实际得到 " +
+ t.getType() + " ('" + t.getLexeme() + "')",
+ t.getLine(), t.getCol());
+ }
+
skipComments(ts);
ts.expectType(TokenType.NEWLINE);
list.add(new ParameterNode(pname, ptype, new NodeContext(line, column, file)));
@@ -229,20 +241,27 @@ public class FunctionParser implements TopLevelParser {
}
/**
- * 解析返回类型声明。
- *
- *
- * 格式为 {@code returns: TYPE},支持前置或行尾注释。
- *
+ * 解析返回类型声明,格式为 `returns: TYPE`,支持注释。
*
* @param ts 当前使用的 {@link TokenStream}。
- * @return 返回类型名称字符串。
+ * @return 返回类型字符串。
*/
private String parseReturnType(TokenStream ts) {
ts.expect("returns");
ts.expect(":");
skipComments(ts);
- Token typeToken = ts.expectType(TokenType.TYPE);
+
+ Token typeToken;
+ if (ts.peek().getType() == TokenType.TYPE || ts.peek().getType() == TokenType.IDENTIFIER) {
+ typeToken = ts.next();
+ } else {
+ var t = ts.peek();
+ throw new UnexpectedToken(
+ "期望 TYPE 或 IDENTIFIER,但实际得到 " +
+ t.getType() + " ('" + t.getLexeme() + "')",
+ t.getLine(), t.getCol());
+ }
+
String rtype = typeToken.getLexeme();
skipComments(ts);
ts.expectType(TokenType.NEWLINE);
@@ -251,14 +270,10 @@ public class FunctionParser implements TopLevelParser {
}
/**
- * 解析函数体区块,直到遇到 {@code end body}。
- *
- *
- * 每一行由对应的语句解析器处理,可嵌套控制结构、返回语句、表达式等。
- *
+ * 解析函数体区块,直到遇到 `end body`。
*
* @param ctx 当前解析上下文。
- * @param ts 当前使用的 {@link TokenStream}。
+ * @param ts 当前使用的 {@link TokenStream}。
* @return 所有函数体语句节点的列表。
*/
private List parseFunctionBody(ParserContext ctx, TokenStream ts) {
@@ -275,9 +290,8 @@ public class FunctionParser implements TopLevelParser {
ts.next();
continue;
}
- if ("end".equals(ts.peek().getLexeme())) {
- break;
- }
+ if ("end".equals(ts.peek().getLexeme())) break;
+
stmts.add(StatementParserFactory.get(ts.peek().getLexeme()).parse(ctx));
}
ts.expect("end");
@@ -293,9 +307,7 @@ public class FunctionParser implements TopLevelParser {
* @param ts 当前使用的 {@link TokenStream}。
*/
private void skipComments(TokenStream ts) {
- while (ts.peek().getType() == TokenType.COMMENT) {
- ts.next();
- }
+ while (ts.peek().getType() == TokenType.COMMENT) ts.next();
}
/**
@@ -304,8 +316,6 @@ public class FunctionParser implements TopLevelParser {
* @param ts 当前使用的 {@link TokenStream}。
*/
private void skipNewlines(TokenStream ts) {
- while (ts.peek().getType() == TokenType.NEWLINE) {
- ts.next();
- }
+ while (ts.peek().getType() == TokenType.NEWLINE) ts.next();
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
index f438719e06abfaf70c209844060c2a4b053ab868..cfd244e81013cd02602a6ca8bdae860b6fcd4df0 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java
@@ -1,10 +1,7 @@
package org.jcnc.snow.compiler.parser.module;
import org.jcnc.snow.compiler.lexer.token.TokenType;
-import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
-import org.jcnc.snow.compiler.parser.ast.FunctionNode;
-import org.jcnc.snow.compiler.parser.ast.ImportNode;
-import org.jcnc.snow.compiler.parser.ast.ModuleNode;
+import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.context.ParserContext;
@@ -12,47 +9,33 @@ import org.jcnc.snow.compiler.parser.context.TokenStream;
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
import org.jcnc.snow.compiler.parser.function.FunctionParser;
import org.jcnc.snow.compiler.parser.statement.DeclarationStatementParser;
+import org.jcnc.snow.compiler.parser.struct.StructParser;
import java.util.ArrayList;
import java.util.List;
/**
- * {@code ModuleParser} 负责解析源码中的模块结构,是顶层结构解析器实现之一。
+ * {@code ModuleParser}
*
- * 模块定义可包含多个导入(import)语句、globals 全局声明和函数定义(function),
- * 导入语句可在模块中任意位置出现,且允许模块体中穿插任意数量的空行(空行会被自动忽略,不影响语法结构)。
- *
- *
- *
- * 典型模块语法结构:
- *
- * module: mymod
- * import ...
- * globals:
- * declare ...
- * function ...
- * ...
- * end module
- *
+ * 顶层结构解析器:负责解析整个源码模块(module ... end module)。
+ *
+ * 支持模块声明、导入(import)、全局变量(globals)、结构体(struct)、函数(function)等顶层语法。
+ * 允许模块体中出现任意数量的空行(自动跳过),顺序自由。
+ * 遇到非法顶层语句或区块会抛出 {@link UnexpectedToken},提示具体位置和原因。
+ *
*
*/
public class ModuleParser implements TopLevelParser {
/**
- * 解析一个模块定义块,返回完整的 {@link ModuleNode} 语法树节点。
+ * 解析一个完整的模块定义块,返回 AST {@link ModuleNode}。
*
- * 解析过程包括:
- *
- * 匹配模块声明起始 {@code module: IDENTIFIER}。
- * 收集模块体内所有 import、globals 和 function 语句,允许穿插空行。
- * 匹配模块结束 {@code end module}。
- *
- * 若遇到未识别的语句,将抛出 {@link UnexpectedToken} 异常,定位错误位置和原因。
+ * 支持空行,允许导入、全局、结构体、函数等多种区块混排。
*
*
- * @param ctx 当前解析上下文(包含词法流等状态)
- * @return 解析得到的 {@link ModuleNode} 实例
- * @throws UnexpectedToken 当模块体中出现未识别的顶层语句时抛出
+ * @param ctx 解析上下文(包含 TokenStream、文件名等信息)
+ * @return 解析生成的 ModuleNode
+ * @throws UnexpectedToken 当模块体中遇到不支持的顶层语句时抛出
*/
@Override
public ModuleNode parse(ParserContext ctx) {
@@ -62,37 +45,53 @@ public class ModuleParser implements TopLevelParser {
int column = ts.peek().getCol();
String file = ctx.getSourceName();
+ // 1) 解析模块声明头部
ts.expect("module");
ts.expect(":");
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
ts.expectType(TokenType.NEWLINE);
+ // 2) 初始化各类节点容器
+ List structs = new ArrayList<>();
List imports = new ArrayList<>();
List globals = new ArrayList<>();
List functions = new ArrayList<>();
+ // 3) 各子区块的专用解析器
+ StructParser structParser = new StructParser();
ImportParser importParser = new ImportParser();
FunctionParser funcParser = new FunctionParser();
DeclarationStatementParser globalsParser = new DeclarationStatementParser();
+ // 4) 进入主循环,直到 end module
while (true) {
// 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
}
+ // 到达模块结尾
if ("end".equals(ts.peek().getLexeme())) {
break;
}
String lex = ts.peek().getLexeme();
switch (lex) {
+ // 解析 import 语句(可多次出现,支持 import 多个模块)
case "import" -> imports.addAll(importParser.parse(ctx));
+
+ // 解析 struct 结构体定义块
+ case "struct" -> structs.add(structParser.parse(ctx));
+
+ // 解析 function 顶层函数定义
case "function" -> functions.add(funcParser.parse(ctx));
+
+ // 解析全局变量声明区块
case "globals" -> {
ts.expect("globals");
ts.expect(":");
ts.expectType(TokenType.NEWLINE);
while (true) {
+ // 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
@@ -100,9 +99,13 @@ public class ModuleParser implements TopLevelParser {
String innerLex = ts.peek().getLexeme();
if ("declare".equals(innerLex)) {
globals.add(globalsParser.parse(ctx));
- } else if ("function".equals(innerLex) || "import".equals(innerLex) || "end".equals(innerLex)) {
+ }
+ // 下一个 function/import/end 开头则结束 globals 区块
+ else if ("function".equals(innerLex) || "import".equals(innerLex) || "end".equals(innerLex)) {
break;
- } else {
+ }
+ // 其余标记为非法内容,抛出异常
+ else {
throw new UnexpectedToken(
"globals 区块中不支持的内容: " + innerLex,
ts.peek().getLine(),
@@ -111,6 +114,7 @@ public class ModuleParser implements TopLevelParser {
}
}
}
+ // 未知或非法顶层内容
case null, default -> throw new UnexpectedToken(
"Unexpected token in module: " + lex,
ts.peek().getLine(),
@@ -119,9 +123,11 @@ public class ModuleParser implements TopLevelParser {
}
}
+ // 5) 匹配模块结尾 "end module"
ts.expect("end");
ts.expect("module");
- return new ModuleNode(name, imports, globals, functions, new NodeContext(line, column, file));
+ // 6) 构造并返回 ModuleNode
+ return new ModuleNode(name, imports, globals, structs, functions, new NodeContext(line, column, file));
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
index 319986aa20e166b2a7b1d225fef55e7a48f09e3f..4a9d3df3d14f3a5f33526595649fcde0434e3e9a 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
@@ -10,75 +10,96 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
* {@code DeclarationStatementParser} 负责解析变量声明语句节点。
*
- * 支持以下两种语法结构:
- *
{@code
- * declare myVar:Integer
- * declare myVar:Integer = 42 + 3
- * }
- * 解析器能够识别多维数组类型(如 {@code int[]}, {@code string[][]}),并支持可选初始化表达式。
- *
- * 每个声明语句均要求以换行符结尾,语法不合法时会抛出异常。
- *
+ *
+ * 支持类型标识符与自定义结构体名
+ * 支持多维数组类型,如 int[][]
+ * 支持带初始值的声明
+ *
*/
public class DeclarationStatementParser implements StatementParser {
/**
- * 解析一条 {@code declare} 语句,生成对应的抽象语法树节点 {@link DeclarationNode}。
- *
- * 支持类型标注和可选初始化表达式。类型部分自动拼接数组维度(如 int[][])。
- *
+ * 解析变量或常量声明语句。
*
- * @param ctx 当前语法解析上下文(包含词法流、错误信息等)
- * @return {@link DeclarationNode} 表示声明语句结构
- * @throws RuntimeException 语法不合法时抛出
+ * @param ctx 语法分析上下文,提供词法单元流与其他辅助功能
+ * @return 解析得到的声明节点 {@link DeclarationNode}
+ * @throws org.jcnc.snow.compiler.parser.context.UnexpectedToken 若语法不合法则抛出异常
*/
@Override
public DeclarationNode parse(ParserContext ctx) {
- // 便捷引用词法 token 流
- var tokens = ctx.getTokens();
+ var tokens = ctx.getTokens(); // 获取词法单元流
- // 获取当前 token 的行号、列号和文件名
- int line = tokens.peek().getLine();
+ // 记录声明语句在源码中的位置信息(行、列、文件名)
+ int line = tokens.peek().getLine();
int column = tokens.peek().getCol();
String file = ctx.getSourceName();
- // 声明语句必须以 "declare" 开头
- tokens.expect("declare");
-
- // 是否声明为常量
- boolean isConst = tokens.match("const");
+ // 判断并消费声明关键字 declare 或 const
+ boolean isConst = false;
+ String first = tokens.peek().getLexeme();
+ if ("declare".equals(first)) {
+ tokens.next(); // 消费 declare
+ // declare 后可选 const,用于声明常量
+ if ("const".equals(tokens.peek().getLexeme())) {
+ isConst = true;
+ tokens.next(); // 消费 const
+ }
+ } else if ("const".equals(first)) {
+ // 支持 const 开头的声明写法
+ isConst = true;
+ tokens.next(); // 消费 const
+ } else {
+ // 不符合语法规则,抛出异常
+ throw new org.jcnc.snow.compiler.parser.context.UnexpectedToken(
+ "声明应以 'declare' 或 'declare const' 开始,而不是 '" + first + "'",
+ tokens.peek().getLine(), tokens.peek().getCol());
+ }
- // 获取变量名称(标识符)
- String name = tokens
- .expectType(TokenType.IDENTIFIER)
- .getLexeme();
+ // 获取变量名(标识符)
+ String name = tokens.expectType(TokenType.IDENTIFIER).getLexeme();
- // 类型标注的冒号分隔符
+ // 检查并消费冒号 “:”
tokens.expect(":");
- // 获取变量类型(类型标识符)
- StringBuilder type = new StringBuilder(
- tokens
- .expectType(TokenType.TYPE)
- .getLexeme()
- );
+ // 解析变量类型(类型标识符或自定义结构体名)
+ StringBuilder type = new StringBuilder();
+ if (tokens.peek().getType() == TokenType.TYPE || tokens.peek().getType() == TokenType.IDENTIFIER) {
+ // 类型可以是基础类型或结构体名
+ type.append(tokens.next().getLexeme());
+ } else {
+ // 类型不是合法的 Token,抛出异常
+ var t = tokens.peek();
+ throw new org.jcnc.snow.compiler.parser.context.UnexpectedToken(
+ "期望的标记类型为 TYPE 或 IDENTIFIER,但实际得到的是 "
+ + t.getType() + " ('" + t.getLexeme() + "')",
+ t.getLine(), t.getCol()
+ );
+ }
- // 消费多维数组类型后缀 "[]"
+ // 处理多维数组类型后缀(支持 int[][] 等类型)
while (tokens.match("[")) {
- tokens.expectType(TokenType.RBRACKET); // 必须配对
- type.append("[]"); // 类型名称拼接 [],如 int[][] 等
+ // 消费左中括号 '[' 后必须跟右中括号 ']'
+ tokens.expectType(TokenType.RBRACKET); // 消费 ']'
+ type.append("[]"); // 追加数组后缀
}
- // 可选初始化表达式(=号右侧)
+ // 可选的初始化表达式(如 = 10)
ExpressionNode init = null;
if (tokens.match("=")) {
+ // 使用 Pratt 解析器解析表达式,获得初始化表达式节点
init = new PrattExpressionParser().parse(ctx);
}
- // 声明语句必须以换行符结尾
+ // 声明语句必须以换行符 NEWLINE 结尾
tokens.expectType(TokenType.NEWLINE);
- // 返回构建好的声明语法树节点
- return new DeclarationNode(name, type.toString(), isConst, init, new NodeContext(line, column, file));
+ // 组装声明节点并返回
+ return new DeclarationNode(
+ name, // 变量/常量名
+ type.toString(), // 类型字符串
+ isConst, // 是否常量
+ init, // 初始化表达式节点(可为 null)
+ new NodeContext(line, column, file) // 源码位置信息
+ );
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
index 955baa743f2c75b2913d93c17c2edfea3bfc53bd..766f53d4524bd7c23fcbaebefa4db77aa6e0a77f 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
@@ -14,17 +14,17 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
* {@code ExpressionStatementParser} 用于解析通用表达式语句(赋值或普通表达式)。
*
- * 支持以下两种语法结构:
+ * 支持以下两种语法结构:
*
{@code
* x = 1 + 2 // 赋值语句
* doSomething() // 一般表达式语句
+ * this.name = n // 将 this.name 赋值语法糖为对 name 的赋值
* }
- *
- * 以标识符开头且后接 {@code =} 时,解析为 {@link AssignmentNode}。
- * 否则视为普通表达式,解析为 {@link ExpressionStatementNode}。
- * 所有表达式语句必须以换行符({@code NEWLINE})结尾。
- *
- * 若语句起始为关键字或空行,将直接抛出异常,防止非法语法进入表达式解析流程。
+ *
+ * - 以标识符开头且后接 '=' 时,解析为 {@link AssignmentNode}。
+ * - 否则先解析为一般表达式;若后续遇到 '=',则回退为“ = ”赋值语句。
+ * - 所有表达式语句必须以换行符(NEWLINE)结尾。
+ *
*/
public class ExpressionStatementParser implements StatementParser {
@@ -33,49 +33,81 @@ public class ExpressionStatementParser implements StatementParser {
*
* @param ctx 当前解析上下文,提供词法流与环境信息
* @return {@link AssignmentNode} 或 {@link ExpressionStatementNode} 语法节点
- * @throws UnexpectedToken 若遇到非法起始(关键字、空行等)
+ * @throws UnexpectedToken 若遇到非法起始(关键字 'end' 等)
*/
@Override
public StatementNode parse(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
- if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
+ // ----------- 起始 token 合法性检查(放宽以支持 this 开头)-----------
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ // 空行不应进入表达式解析,直接抛出异常
throw new UnexpectedToken(
- "无法解析以关键字开头的表达式: " + ts.peek().getLexeme(),
+ "无法解析以空行开头的表达式",
ts.peek().getLine(),
ts.peek().getCol()
);
}
+ if (ts.peek().getType() == TokenType.KEYWORD) {
+ String kw = ts.peek().getLexeme();
+ // 仅允许 this 作为表达式起始;其它关键字(如 end/if/else 等)仍禁止
+ if (!"this".equals(kw)) {
+ throw new UnexpectedToken(
+ "无法解析以关键字开头的表达式: " + kw,
+ ts.peek().getLine(),
+ ts.peek().getCol()
+ );
+ }
+ }
int line = ts.peek().getLine();
int column = ts.peek().getCol();
String file = ctx.getSourceName();
- // 简单形式: IDENTIFIER = expr
+ // ------------- 简单形式: IDENTIFIER = expr -------------
+ // 快速路径:如 "a = ...",直接识别为赋值语句,无需完整表达式树回退
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
- String varName = ts.next().getLexeme();
- ts.expect("=");
- ExpressionNode value = new PrattExpressionParser().parse(ctx);
+ String varName = ts.next().getLexeme(); // 消费 IDENTIFIER
+ ts.expect("="); // 消费 '='
+ ExpressionNode value = new PrattExpressionParser().parse(ctx); // 解析右侧表达式
ts.expectType(TokenType.NEWLINE);
+ // 返回简单变量赋值节点
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
}
- // 尝试解析更通用的左值形式(支持下标): =
+ // ------------- 通用形式: [= ] -------------
+ // 先解析潜在“左值表达式”或普通表达式
ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
+
+ // 若遇到等号,则尝试回退为赋值语句(兼容更复杂的左值表达式)
if ("=".equals(ts.peek().getLexeme())) {
- ts.next(); // consume '='
- ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
+ ts.next(); // 消费 '='
+ ExpressionNode rhs = new PrattExpressionParser().parse(ctx); // 解析右值表达式
ts.expectType(TokenType.NEWLINE);
- // 根据左值类型构造具体赋值节点
+
+ // 根据左值 AST 类型,生成不同赋值节点
if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode id) {
+ // 变量名赋值:a = rhs
return new AssignmentNode(id.name(), rhs, new NodeContext(line, column, file));
+
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IndexExpressionNode idx) {
+ // 下标赋值:a[i] = rhs
return new org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode(idx, rhs, new NodeContext(line, column, file));
+
+ } else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.MemberExpressionNode mem
+ && mem.object() instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode oid
+ && "this".equals(oid.name())) {
+ // 支持:this.field = rhs
+ // 语法糖:降级为对当前作用域同名变量的赋值,相当于 "field = rhs"
+ return new AssignmentNode(mem.member(), rhs, new NodeContext(line, column, file));
+
} else {
+ // 其它成员赋值(如 a.b = ...)不支持,报错
throw new UnexpectedToken("不支持的赋值左值类型: " + lhs.getClass().getSimpleName(), line, column);
}
}
- // 普通表达式语句
+
+ // 不是赋值,则当作普通表达式语句处理
ts.expectType(TokenType.NEWLINE);
return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/struct/StructParser.java b/src/main/java/org/jcnc/snow/compiler/parser/struct/StructParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..e57e2c96f7b1baa3a064828a410d0de2e59e2dcd
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/struct/StructParser.java
@@ -0,0 +1,293 @@
+package org.jcnc.snow.compiler.parser.struct;
+
+import org.jcnc.snow.compiler.lexer.token.TokenType;
+import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.ast.ParameterNode;
+import org.jcnc.snow.compiler.parser.ast.StructNode;
+import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
+import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
+import org.jcnc.snow.compiler.parser.base.TopLevelParser;
+import org.jcnc.snow.compiler.parser.context.ParserContext;
+import org.jcnc.snow.compiler.parser.context.TokenStream;
+import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
+import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
+import org.jcnc.snow.compiler.parser.function.FunctionParser;
+import org.jcnc.snow.compiler.parser.statement.DeclarationStatementParser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@code StructParser}
+ *
+ * 解析 struct ... end struct
结构体声明块的顶层语法解析器。
+ *
+ * 支持解析结构体字段、构造函数(init)、结构体方法列表。
+ * 按顺序支持 fields/init/function 三种结构体内部块。
+ * 语法出错时抛出 {@link UnexpectedToken} 异常,便于错误定位。
+ *
+ *
+ */
+public class StructParser implements TopLevelParser {
+
+ /**
+ * 解析结构体声明块,并返回 AST 节点 {@link StructNode}。
+ *
+ *
+ * 该方法解析结构体声明的头部,结构体内部的字段块(fields)、构造函数块(init)、方法块(function)以及结束标记(end struct)。
+ *
+ *
+ * @param ctx 解析上下文,包含 TokenStream 和符号表等信息。
+ * @return 解析得到的 {@link StructNode},包含结构体的名称、字段、构造函数、方法等信息。
+ */
+ @Override
+ public StructNode parse(ParserContext ctx) {
+ TokenStream ts = ctx.getTokens();
+
+ int line = ts.peek().getLine();
+ int col = ts.peek().getCol();
+ String file = ctx.getSourceName();
+
+ /* -------- 解析头部 -------- */
+ ts.expect("struct");
+ ts.expect(":");
+ String structName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
+
+ // 解析可选 extends
+ String parentName = null;
+ if ("extends".equals(ts.peek().getLexeme())) {
+ ts.expect("extends");
+ parentName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
+ }
+
+ ts.expectType(TokenType.NEWLINE);
+
+ /* -------- 初始化容器 -------- */
+ List fields = new ArrayList<>();
+ List inits = new ArrayList<>();
+ List methods = new ArrayList<>();
+
+ DeclarationStatementParser declParser = new DeclarationStatementParser();
+ FunctionParser funcParser = new FunctionParser();
+
+ /* -------- 主循环:依次解析 struct 块内部字段、构造、方法 -------- */
+ while (true) {
+ /* 跳过空行 */
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+
+ String lex = ts.peek().getLexeme();
+ switch (lex) {
+ /* ---------- fields 块 ---------- */
+ case "fields" -> {
+ ts.expect("fields");
+ ts.expect(":");
+ ts.expectType(TokenType.NEWLINE);
+ // 字段块不强制 'end fields',遇到非 declare 则退出
+ while (true) {
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+ if ("declare".equals(ts.peek().getLexeme())) {
+ // 字段声明使用通用 DeclarationStatementParser(其已支持 TYPE/IDENTIFIER 作为类型)
+ fields.add(declParser.parse(ctx));
+ } else {
+ // 非 declare 开头则结束 fields
+ break;
+ }
+ }
+ }
+
+ /* ---------- 构造函数 init ---------- */
+ case "init" -> {
+ FunctionNode ctor = parseInit(ctx, structName);
+
+ // 按参数个数去重
+ for (FunctionNode ex : inits) {
+ if (ex.parameters().size() == ctor.parameters().size()) {
+ throw new UnexpectedToken(
+ "重复定义 init 构造函数 (参数数量冲突)",
+ ts.peek().getLine(), ts.peek().getCol());
+ }
+ }
+ inits.add(ctor);
+ }
+
+ /* ---------- 普通方法 function ---------- */
+ case "function" -> methods.add(funcParser.parse(ctx));
+
+ /* ---------- struct 结束 ---------- */
+ case "end" -> {
+ ts.expect("end");
+ ts.expect("struct");
+ return new StructNode(structName, parentName,
+ fields, inits, methods,
+ new NodeContext(line, col, file));
+ }
+
+ /* ---------- 非法内容 ---------- */
+ default -> throw new UnexpectedToken(
+ "struct 块内不支持的标记: " + lex,
+ ts.peek().getLine(), ts.peek().getCol());
+ }
+ }
+ }
+
+ /* ====================================================================== */
+ /* -------------------------- 构造函数 init -------------------------- */
+ /* ====================================================================== */
+
+ /**
+ * 解析结构体构造函数 init 块,返回 FunctionNode。
+ *
+ * 允许包含 params 和 body 两部分,顺序不限;以 "end init" 结束。
+ *
+ *
+ * @param ctx 解析上下文
+ * @param structName 结构体名称,用于构造函数唯一命名
+ * @return 表示构造函数的 FunctionNode
+ */
+ private FunctionNode parseInit(ParserContext ctx, String structName) {
+ TokenStream ts = ctx.getTokens();
+
+ int line = ts.peek().getLine();
+ int col = ts.peek().getCol();
+ String file = ctx.getSourceName();
+
+ ts.expect("init");
+ ts.expect(":");
+ ts.expectType(TokenType.NEWLINE);
+
+ /* -------- 初始化参数和方法体容器 -------- */
+ List params = new ArrayList<>();
+ List body = new ArrayList<>();
+
+ // 主循环:支持 params/body 两块,顺序不限
+ while (true) {
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+
+ String lex = ts.peek().getLexeme();
+ switch (lex) {
+ case "params" -> params.addAll(parseParams(ctx));
+ case "body" -> body.addAll(parseBody(ctx));
+ case "end" -> {
+ ts.expect("end");
+ ts.expect("init");
+ // 构造唯一命名的 FunctionNode
+ return new FunctionNode(
+ structName + ".__init__", // 唯一命名
+ params,
+ "void",
+ body,
+ new NodeContext(line, col, file));
+ }
+ default -> throw new UnexpectedToken(
+ "init 块内不支持的标记: " + lex,
+ ts.peek().getLine(), ts.peek().getCol());
+ }
+ }
+ }
+
+ /* ---------------- params: 参数块解析 ---------------- */
+
+ /**
+ * 解析 params 块,返回参数列表。
+ *
+ * 且在类型位置同时接受内建类型({@link TokenType#TYPE})与自定义标识符({@link TokenType#IDENTIFIER})。
+ *
+ * @param ctx 解析上下文
+ * @return 解析得到的参数节点列表
+ */
+ private List parseParams(ParserContext ctx) {
+ TokenStream ts = ctx.getTokens();
+
+ ts.expect("params");
+ ts.expect(":");
+ ts.expectType(TokenType.NEWLINE);
+
+ List list = new ArrayList<>();
+ while (true) {
+ // 跳过空行
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+
+ // 碰到 body / end / returns 等其他小节,说明 params 结束
+ String lookaheadLex = ts.peek().getLexeme();
+ if ("body".equals(lookaheadLex) || "end".equals(lookaheadLex) || "returns".equals(lookaheadLex)) {
+ break;
+ }
+
+ int line = ts.peek().getLine();
+ int col = ts.peek().getCol();
+
+ // 支持两种前缀:有 declare / 无 declare
+ boolean hasDeclare = "declare".equals(ts.peek().getLexeme());
+ if (hasDeclare) {
+ ts.expect("declare");
+ }
+
+ // 参数名
+ String pName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
+ ts.expect(":");
+
+ // 参数类型:既可为 TYPE(内置),也可为 IDENTIFIER(自定义)
+ String pType;
+ if (ts.peek().getType() == TokenType.TYPE || ts.peek().getType() == TokenType.IDENTIFIER) {
+ pType = ts.next().getLexeme();
+ } else {
+ var t = ts.peek();
+ throw new UnexpectedToken(
+ "期望的标记类型为 TYPE 或 IDENTIFIER,但实际得到的是 " +
+ t.getType() + " ('" + t.getLexeme() + "')",
+ t.getLine(), t.getCol());
+ }
+
+ ts.expectType(TokenType.NEWLINE);
+ list.add(new ParameterNode(pName, pType, new NodeContext(line, col, ctx.getSourceName())));
+ }
+ return list;
+ }
+
+ /* ---------------- body: 方法体块解析 ---------------- */
+
+ /**
+ * 解析 body 块,返回语句节点列表。
+ *
+ * @param ctx 解析上下文
+ * @return 解析得到的方法体语句列表
+ */
+ private List parseBody(ParserContext ctx) {
+ TokenStream ts = ctx.getTokens();
+
+ ts.expect("body");
+ ts.expect(":");
+ ts.expectType(TokenType.NEWLINE);
+
+ List body = new ArrayList<>();
+
+ // 循环读取每一条语句,直到 end body
+ while (true) {
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+ if ("end".equals(ts.peek().getLexeme())) break; // body 块结束
+
+ var parser = StatementParserFactory.get(ts.peek().getLexeme());
+ body.add(parser.parse(ctx));
+ }
+
+ ts.expect("end");
+ ts.expect("body");
+ return body;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
index b3094c1b9b8a82fadc230eaa3f3aa03ec7f37cca..3fc8631c0c704f5d621b8739335a2c6cb3154d0b 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
@@ -5,7 +5,10 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.Node;
import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
/**
* {@code ASTJsonSerializer} 是抽象语法树(AST)序列化工具类。
@@ -83,7 +86,8 @@ public class ASTJsonSerializer {
return switch (n) {
// 模块节点
case ModuleNode(
- String name, List imports, List globals, List functions, NodeContext _
+ String name, List imports, List globals, List structs,
+ List functions, NodeContext _
) -> {
Map map = newNodeMap("Module");
map.put("name", name);
@@ -105,9 +109,47 @@ public class ASTJsonSerializer {
for (FunctionNode f : functions) {
funcs.add(nodeToMap(f));
}
+ List lStructs = new ArrayList<>();
+ structs.forEach(s -> lStructs.add(nodeToMap(s)));
+ map.put("structs", lStructs);
map.put("functions", funcs);
yield map;
}
+
+ // Struct 节点(多构造支持)
+ case StructNode(
+ String name, String parent, List fields, List inits,
+ List methods, NodeContext _
+ ) -> {
+ Map map = newNodeMap("Struct");
+ map.put("name", name);
+ map.put("parent", parent);
+
+ List lFields = new ArrayList<>();
+ fields.forEach(d -> lFields.add(Map.of(
+ "type", "Field",
+ "name", d.getName(),
+ "varType", d.getType())));
+ map.put("fields", lFields);
+
+ // 多构造函数序列化为 inits 数组
+ List lInits = new ArrayList<>();
+ if (inits != null) {
+ for (FunctionNode ctor : inits) {
+ lInits.add(nodeToMap(ctor));
+ }
+ }
+ map.put("inits", lInits);
+
+ List lMethods = new ArrayList<>();
+ if (methods != null) {
+ for (FunctionNode f : methods) lMethods.add(nodeToMap(f));
+ }
+ map.put("methods", lMethods);
+ yield map;
+ }
+
+
// 函数定义节点
case FunctionNode f -> {
Map map = newNodeMap("Function");
@@ -189,13 +231,12 @@ public class ASTJsonSerializer {
private static Object exprToMap(ExpressionNode expr) {
return switch (expr) {
// 二元表达式
- case BinaryExpressionNode(
- ExpressionNode left, String operator, ExpressionNode right, NodeContext _
- ) -> exprMap("BinaryExpression",
- "left", exprToMap(left),
- "operator", operator,
- "right", exprToMap(right)
- );
+ case BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right, NodeContext _) ->
+ exprMap("BinaryExpression",
+ "left", exprToMap(left),
+ "operator", operator,
+ "right", exprToMap(right)
+ );
// 一元表达式
case UnaryExpressionNode(String operator, ExpressionNode operand, NodeContext _) ->
exprMap("UnaryExpression",
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
index c71f54ad7bd51a2317be1453794ed026f9ffed4e..0e0f5b76d654c78ef1ac210467575cd3b3eee9df 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
@@ -1,44 +1,76 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
-import org.jcnc.snow.compiler.parser.ast.*;
+import org.jcnc.snow.compiler.parser.ast.CallExpressionNode;
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
+import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
-import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
import org.jcnc.snow.compiler.semantic.core.Context;
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
+import org.jcnc.snow.compiler.semantic.symbol.Symbol;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.FunctionType;
+import org.jcnc.snow.compiler.semantic.type.StructType;
import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.ArrayList;
import java.util.List;
/**
- * {@code CallExpressionAnalyzer} 是函数调用表达式的语义分析器。
- *
- * 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式,执行如下操作:
+ * {@code CallExpressionAnalyzer} 是函数调用表达式 ({@link CallExpressionNode}) 的语义分析器。
+ *
+ *
它负责处理所有形式的调用表达式(如 {@code callee(arg1, arg2, ...)}),并执行如下操作:
*
- * 识别调用目标(支持模块成员函数调用和当前模块函数调用,也支持自动在所有已导入模块中查找唯一同名函数);
- * 根据被调用函数的参数签名检查实参数量和类型的兼容性;
- * 支持数值参数的宽化转换(如 int → double);
- * 支持数值到字符串的隐式转换(自动视为调用 {@code to_string});
- * 在发生类型不匹配、未导入模块或函数未定义等情况下记录语义错误。
- * 新增:以"_"开头的函数名只允许在本模块访问,禁止跨模块访问。
+ * 识别调用目标:支持三种调用方式
+ *
+ * 模块函数调用: {@code module.func(...)}
+ * 结构体实例方法调用: {@code instance.method(...)}
+ * 普通函数调用(当前模块或导入模块): {@code func(...)}
+ *
+ *
+ * 在函数解析时遵循如下规则:
+ *
+ * 若是模块调用,必须确认模块已导入。
+ * 若是结构体实例调用,需先解析左侧表达式类型并确认方法存在。
+ * 若是普通函数调用,优先在当前模块中查找,若未找到,则尝试唯一导入模块解析。
+ *
+ *
+ * 参数检查与类型推断:
+ *
+ * 检查实参与形参数量是否一致。
+ * 检查类型兼容性,支持数值宽化转换 (int → double)。
+ * 支持数值到字符串的隐式转换(自动视为调用 {@code to_string})。
+ *
+ *
+ * 错误处理:
+ *
+ * 函数/方法未定义时记录 {@link SemanticError}。
+ * 访问未导入的模块时报错。
+ * 跨模块访问私有函数(以 "_" 开头)时报错。
+ * 参数数量或类型不匹配时报错。
+ *
+ *
+ * 最终返回函数的返回类型;若分析过程中存在错误,返回 {@link BuiltinType#INT} 作为默认回退类型。
*
+ *
+ * 规则:
+ * - 外部模块函数必须显式写 `模块.函数`,不再支持未限定名。
+ * - 跨模块访问以下划线开头的私有函数禁止。
*/
public class CallExpressionAnalyzer implements ExpressionAnalyzer {
/**
- * 分析函数调用表达式并推断其类型。
+ * 分析函数调用表达式,推断返回类型并执行语义检查。
*
* @param ctx 当前语义分析上下文,提供日志、错误记录、模块访问等功能。
* @param mi 当前模块信息,用于函数查找及模块依赖判断。
- * @param fn 当前分析的函数节点。
- * @param locals 局部符号表,用于变量查找。
+ * @param fn 当前正在分析的函数节点(函数作用域)。
+ * @param locals 局部符号表,用于变量和结构体实例查找。
* @param call 待分析的函数调用表达式节点。
- * @return 表达式的返回类型。如果存在语义错误,默认返回 {@code BuiltinType.INT}。
+ * @return 调用表达式的返回类型;若存在语义错误,返回 {@link BuiltinType#INT}。
*/
@Override
public Type analyze(Context ctx,
@@ -46,109 +78,149 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer candidates = new ArrayList<>();
+ for (String imp : mi.getImports()) {
+ ModuleInfo mod = ctx.getModules().get(imp);
+ if (mod != null && mod.getFunctions().containsKey(functionName)) {
+ candidates.add(mod.getName());
+ }
+ }
+ if (!candidates.isEmpty()) {
+ ctx.getErrors().add(new SemanticError(idNode,
+ "外部模块函数调用必须写为 模块.函数,例如: "
+ + candidates.get(0) + "." + functionName + "(...)"));
+ return BuiltinType.INT;
+ }
+ }
}
- // 不支持的 callee 形式
+ // ===== 3. 其它情况,不支持 =====
else {
ctx.getErrors().add(new SemanticError(callee,
"不支持的调用方式: " + callee));
- ctx.log("错误: 不支持的调用方式 " + callee);
return BuiltinType.INT;
}
- // -------------------------
- // 私有函数访问控制
- // -------------------------
- // 如果函数名以"_"开头,且不是在本模块调用,则报错
- if (functionName.startsWith("_") && !target.getName().equals(mi.getName())) {
+ // -------- 访问控制检查 --------
+ if (functionName != null && funcType != null &&
+ functionName.startsWith("_") &&
+ !targetModule.getName().equals(mi.getName())) {
ctx.getErrors().add(new SemanticError(callee,
- "无法访问模块私有函数: " + target.getName() + "." + functionName
- + "(下划线开头的函数只允许在定义模块内访问)"));
- ctx.log("错误: 试图跨模块访问私有函数 " + target.getName() + "." + functionName);
+ "无法访问模块私有函数: " + targetModule.getName() + "." + functionName));
return BuiltinType.INT;
}
- // 查找目标函数签名(先在当前模块/指定模块查找)
- FunctionType ft = target.getFunctions().get(functionName);
-
- // 未找到则报错
- if (ft == null) {
+ // -------- 函数是否存在 --------
+ if (funcType == null) {
ctx.getErrors().add(new SemanticError(callee,
"函数未定义: " + functionName));
- ctx.log("错误: 函数未定义 " + functionName);
return BuiltinType.INT;
}
- // 分析所有实参并获取类型
+ // -------- 分析实参类型 --------
List args = new ArrayList<>();
for (ExpressionNode arg : call.arguments()) {
args.add(ctx.getRegistry().getExpressionAnalyzer(arg)
.analyze(ctx, mi, fn, locals, arg));
}
- // 参数数量检查
- if (args.size() != ft.paramTypes().size()) {
+ if (args.size() != funcType.paramTypes().size()) {
ctx.getErrors().add(new SemanticError(call,
- "参数数量不匹配: 期望 " + ft.paramTypes().size()
- + " 个, 实际 " + args.size() + " 个"));
- ctx.log("错误: 参数数量不匹配: 期望 "
- + ft.paramTypes().size() + ", 实际 " + args.size());
-
+ "参数数量不匹配: 期望 " + funcType.paramTypes().size()
+ + ", 实际 " + args.size()));
} else {
- // 参数类型检查与转换支持
for (int i = 0; i < args.size(); i++) {
- Type expected = ft.paramTypes().get(i);
- Type actual = args.get(i);
+ Type expected = funcType.paramTypes().get(i);
+ Type actual = args.get(i);
- // 完全兼容或数值宽化转换
boolean ok = expected.isCompatible(actual)
|| (expected.isNumeric() && actual.isNumeric()
&& Type.widen(actual, expected) == expected);
- // 支持将数值自动转换为字符串
if (!ok && expected == BuiltinType.STRING && actual.isNumeric()) {
- ctx.log(String.format(
- "隐式将参数 %d 的数值类型 %s 转换为 string (to_string)",
- i, actual
- ));
- ok = true;
+ ok = true; // 自动数值→string
}
- // 类型不匹配,记录语义错误
if (!ok) {
ctx.getErrors().add(new SemanticError(call,
- String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
- i, expected, actual)));
- ctx.log("错误: 参数类型不匹配 (位置 " + i + "): 期望 "
- + expected + ", 实际 " + actual);
+ "参数类型不匹配 (位置 " + i + "): 期望 " + expected + ", 实际 " + actual));
}
}
}
- // 返回函数的返回类型作为整个调用表达式的类型
- ctx.log("函数调用类型: 返回 " + ft.returnType());
- return ft.returnType();
+ // -------- 返回类型 --------
+ ctx.log("函数调用类型: 返回 " + funcType.returnType());
+ return funcType.returnType();
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/MemberExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/MemberExpressionAnalyzer.java
index 5080545df6dafbd80d1442315c5a6a28b603c953..42858930559859300d75ad5f460b0ec62aee7191 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/MemberExpressionAnalyzer.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/MemberExpressionAnalyzer.java
@@ -11,39 +11,49 @@ import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
+import org.jcnc.snow.compiler.semantic.type.FunctionType;
+import org.jcnc.snow.compiler.semantic.type.StructType;
import org.jcnc.snow.compiler.semantic.type.Type;
/**
- * {@code MemberExpressionAnalyzer} 用于分析模块成员访问表达式的类型和语义。
+ * {@code MemberExpressionAnalyzer}
*
*
- * 当前实现支持 ModuleName.constOrVar
形式的跨模块常量/全局变量访问,
- * 能根据目标模块的全局符号表,返回准确的类型信息,完全支持跨模块类型推断。
- *
- * 对于非模块成员的访问(如对象.属性、多级 a.b.c),暂不支持,遇到时将报告语义错误。
+ * 负责成员访问表达式的语义分析与类型推断。
+ * 典型形如 a.b
、this.x
、Module.member
。
*
*
*
- * 核心特性:
- *
- * 校验模块是否存在、是否已导入(或自身);
- * 跨模块访问目标模块的全局符号表,查找指定成员符号及其类型;
- * 若成员不存在,报告“模块成员未定义”语义错误;
- * 暂不支持更复杂的对象成员访问,遇到将报“不支持的成员访问对象类型”错误。
- *
+ * 支持三类成员访问方式:
*
+ *
+ * 当前实例字段语法糖 :如 this.x
,等价于访问当前作用域下名为 x
的变量或字段。
+ * 跨模块成员访问 :如 ModuleName.member
,常用于引用其他模块的全局变量、常量、函数等。
+ * 结构体实例成员/方法访问 :如 a.b
或 a.method
,a 为结构体变量,b 为字段或方法。
+ *
*/
public class MemberExpressionAnalyzer implements ExpressionAnalyzer {
/**
- * 语义分析模块成员访问表达式。
+ * 分析成员表达式,并返回其类型(如字段类型、方法类型等)。
*
- * @param ctx 全局语义分析上下文,持有所有模块及错误记录
- * @param mi 当前模块信息(用于判断导入关系)
+ * @param ctx 全局语义分析上下文
+ * @param mi 当前模块信息
* @param fn 当前函数节点
- * @param locals 当前局部符号表
- * @param expr 当前要分析的成员表达式(如 ModuleA.a)
- * @return 成员表达式的类型;出错时类型降级为 int,并记录语义错误
+ * @param locals 局部符号表(当前作用域的变量、形参等)
+ * @param expr 当前成员访问表达式(形如 obj.member)
+ * @return 该表达式的推断类型,如失败则返回 {@link BuiltinType#INT}
+ *
+ *
+ * 主要处理流程分为四大分支:
+ *
+ * this.x 语法糖(字段或变量访问)
+ * ModuleName.member 跨模块成员访问
+ * 结构体实例的成员/方法访问
+ * 其它对象成员(不支持)
+ *
+ * 出错时会注册语义错误,返回 int 作为降级类型。
+ *
*/
@Override
public Type analyze(Context ctx,
@@ -54,41 +64,117 @@ public class MemberExpressionAnalyzer implements ExpressionAnalyzer
+ * 负责“对象创建表达式”new T(args...)
的语义分析与类型推断。
+ *
+ * 分析 new 关键字创建结构体对象的表达式。
+ * 校验目标类型是否为结构体,参数数量与类型是否匹配构造函数。
+ * 出错时会收集详细的语义错误信息。
+ *
+ *
+ *
+ * 表达式类型: 返回被 new 的结构体类型 T
,如失败则降级为 int。
+ *
+ */
+public class NewExpressionAnalyzer implements ExpressionAnalyzer {
+
+ /**
+ * 语义分析:推断 new 表达式的类型、参数检查、构造函数合法性校验。
+ *
+ * @param ctx 全局语义分析上下文
+ * @param mi 当前模块信息
+ * @param fn 当前所在函数节点
+ * @param locals 局部符号表(当前作用域变量/形参)
+ * @param expr new 表达式节点
+ * @return 分析推断得到的类型;如出错则降级为 int 类型
+ */
+ @Override
+ public Type analyze(Context ctx,
+ ModuleInfo mi,
+ FunctionNode fn,
+ SymbolTable locals,
+ NewExpressionNode expr) {
+
+ final String typeName = expr.typeName();
+
+ // 1. 解析目标类型(通过语义上下文统一入口,支持别名、跨模块等情况)
+ Type parsed = ctx.parseType(typeName);
+ if (parsed == null) {
+ // 类型不存在,报错并降级
+ ctx.errors().add(new SemanticError(expr, "未知类型: " + typeName));
+ return BuiltinType.INT; // 兜底,避免连锁报错
+ }
+ if (!(parsed instanceof StructType st)) {
+ // 非结构体类型不能用 new
+ ctx.errors().add(new SemanticError(expr, "只有结构体类型才能使用 new: " + parsed));
+ return BuiltinType.INT;
+ }
+
+ // 2. 分析所有实参的类型
+ List argTypes = new ArrayList<>();
+ for (ExpressionNode a : expr.arguments()) {
+ Type at = ctx.getRegistry()
+ .getExpressionAnalyzer(a)
+ .analyze(ctx, mi, fn, locals, a);
+ if (at == null) {
+ // 兜底处理,防止后续 NPE
+ at = BuiltinType.INT;
+ }
+ argTypes.add(at);
+ }
+
+ // 3. 选择并检查结构体构造函数(init)——支持重载:按“参数个数”匹配
+ FunctionType ctor = st.getConstructor(argTypes.size());
+
+ if (ctor == null) {
+ // 若该结构体完全未声明任何 init,且调用为 0 实参,则允许隐式默认构造(保留旧行为)
+ if (st.getConstructors().isEmpty() && argTypes.isEmpty()) {
+ return st;
+ }
+ // 否则报错:未找到对应形参个数的构造函数
+ ctx.errors().add(new SemanticError(expr,
+ "未找到参数个数为 " + argTypes.size() + " 的构造函数"));
+ return st;
+ }
+
+ // 3.1. 构造函数参数类型兼容性检查(包括数值类型宽化)
+ List expectedParams = ctor.paramTypes();
+ for (int i = 0; i < expectedParams.size(); i++) {
+ Type expected = expectedParams.get(i); // 构造函数声明的参数类型
+ Type actual = argTypes.get(i); // 实际传入的实参类型
+
+ boolean compatible = expected.isCompatible(actual); // 直接类型兼容
+ boolean widenOK = expected.isNumeric()
+ && actual.isNumeric()
+ && expected.equals(Type.widen(actual, expected)); // 支持数值类型自动宽化
+
+ if (!compatible && !widenOK) {
+ // 实参类型不兼容也无法宽化,报错
+ ctx.errors().add(new SemanticError(expr,
+ String.format("构造函数参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
+ i, expected, actual)));
+ }
+ }
+
+ // 4. new 表达式的类型就是结构体类型
+ return st;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
index 74b4d3f3f4854f5ac56114ee6e4f05f6fff87230..9c6e1b36cbf2098db666dd9c8b861365d56180a7 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
@@ -56,17 +56,11 @@ public final class AnalyzerRegistrar {
registry.registerExpressionAnalyzer(IdentifierNode.class, new IdentifierAnalyzer());
registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer());
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
-
- registry.registerExpressionAnalyzer(ArrayLiteralNode.class, new ArrayLiteralAnalyzer());
- registry.registerExpressionAnalyzer(IndexExpressionNode.class,new IndexExpressionAnalyzer()); // ★ 关键行
+ registry.registerExpressionAnalyzer(ArrayLiteralNode.class, new ArrayLiteralAnalyzer());
+ registry.registerExpressionAnalyzer(IndexExpressionNode.class, new IndexExpressionAnalyzer());
registry.registerStatementAnalyzer(IndexAssignmentNode.class, new IndexAssignmentAnalyzer());
-
-
- // ---------- 注册一元表达式分析器 ----------
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
-
- // ---------- 成员访问表达式 ----------
- registry.registerExpressionAnalyzer(MemberExpressionNode.class,
- new MemberExpressionAnalyzer());
+ registry.registerExpressionAnalyzer(NewExpressionNode.class, new NewExpressionAnalyzer());
+ registry.registerExpressionAnalyzer(MemberExpressionNode.class, new MemberExpressionAnalyzer());
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
index 6a69b672a5cfd42db0d3d0ed5492ae7ad6e11bc8..c76a34660e4979e0fef26dfc4c8ddfadb0b1f9fe 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
@@ -9,47 +9,61 @@ import java.util.List;
import java.util.Map;
/**
- * {@code Context} 表示语义分析阶段的全局上下文环境。
+ * {@code Context} 表示语义分析阶段的全局上下文环境,贯穿于编译器语义分析流程中。
*
- * 该类贯穿整个语义分析流程,集中管理以下内容:
+ * 主要负责:
*
- * 模块信息管理(所有已加载模块,包括源模块和内置模块);
- * 错误收集(集中存储语义分析期间产生的 {@link SemanticError});
- * 日志输出控制(可选,支持调试信息);
- * 分析器调度(通过 {@link AnalyzerRegistry} 分发对应分析器);
+ * 模块信息的统一管理(含模块、结构体、类型);
+ * 收集和存储语义分析阶段产生的所有错误;
+ * 根据配置输出调试日志;
+ * 调度各类语义分析器执行;
+ * 类型解析(包括内建类型、数组类型、结构体等自定义类型)。
*
- *
- * 提供便捷的 getter 方法和类型解析工具方法。
- *
+ * 该类为所有分析器提供便捷的访问、查找和工具方法。
*/
public class Context {
/**
- * 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用。
+ * 所有模块信息表。
+ *
+ * 键为模块名,值为 {@link ModuleInfo} 实例。用于支持跨模块类型/结构体引用。
*/
private final Map modules;
/**
- * 错误列表:语义分析过程中收集的所有 {@link SemanticError}。
+ * 语义分析期间收集的所有错误对象。
+ *
+ * 每当发现语义错误时,将 {@link SemanticError} 添加到该列表。
*/
private final List errors;
/**
- * 日志开关:若为 true,将启用 {@link #log(String)} 输出日志信息。
+ * 日志输出开关。
+ *
+ * 为 true 时,调用 {@link #log(String)} 会输出日志信息,用于调试。
*/
private final boolean verbose;
/**
- * 语义分析器注册表:用于按节点类型动态调度分析器。
+ * 语义分析器注册表。
+ *
+ * 用于根据语法树节点类型动态分派对应的分析器。
*/
private final AnalyzerRegistry registry;
/**
- * 构造语义分析上下文对象。
+ * 当前正在处理的模块名。
+ *
+ * 主要用于类型解析时限定结构体查找的作用域。
+ */
+ private String currentModuleName;
+
+ /**
+ * 构造一个新的语义分析上下文实例。
*
- * @param modules 已注册的模块信息集合
- * @param errors 错误收集器,分析器将所有语义错误写入此列表
- * @param verbose 是否启用调试日志输出
- * @param registry 分析器注册表,提供类型到分析器的映射与调度能力
+ * @param modules 所有已加载模块的映射表
+ * @param errors 用于收集错误的列表
+ * @param verbose 是否输出日志
+ * @param registry 分析器注册表
*/
public Context(Map modules,
List errors,
@@ -61,102 +75,128 @@ public class Context {
this.registry = registry;
}
- // ------------------ 模块信息 ------------------
+ // ==== Getter 方法(基本访问器) ====
/**
- * 获取所有模块信息映射表。
- *
- * @return 模块名到模块信息({@link ModuleInfo})的映射表
+ * 获取所有模块信息表(模块名 → ModuleInfo)。
*/
- public Map getModules() {
+ public Map modules() {
return modules;
}
/**
- * 模块信息 getter(快捷方式)。
- *
- * @return 模块名到模块信息({@link ModuleInfo})的映射表
+ * 获取所有模块信息表(与 {@link #modules()} 等价)。
*/
- public Map modules() {
+ public Map getModules() {
return modules;
}
- // ------------------ 错误收集 ------------------
-
/**
- * 获取语义分析过程中记录的所有错误。
- *
- * @return 错误列表
+ * 获取语义分析期间收集的所有错误。
*/
- public List getErrors() {
+ public List errors() {
return errors;
}
/**
- * 错误列表 getter(快捷方式)。
- *
- * @return 错误列表
+ * 获取语义分析期间收集的所有错误(与 {@link #errors()} 等价)。
*/
- public List errors() {
+ public List getErrors() {
return errors;
}
- // ------------------ 分析器注册表 ------------------
-
/**
- * 获取分析器注册表,用于分发语句与表达式分析器。
- *
- * @return {@link AnalyzerRegistry} 实例
+ * 获取语义分析器注册表。
*/
public AnalyzerRegistry getRegistry() {
return registry;
}
/**
- * 注册表 getter(快捷方式)。
+ * 输出语义分析日志信息(受 verbose 控制)。
*
- * @return {@link AnalyzerRegistry} 实例
+ * @param msg 日志消息内容
*/
- public AnalyzerRegistry registry() {
- return registry;
+ public void log(String msg) {
+ if (verbose) System.out.println("[semantic] " + msg);
}
- // ------------------ 日志输出 ------------------
+ // ==== 当前模块管理 ====
/**
- * 打印日志信息,仅当 {@code verbose} 为 true 时生效。
+ * 获取当前正在分析的模块名。
*
- * @param msg 日志内容
+ * @return 当前模块名,可能为 null
*/
- public void log(String msg) {
- if (verbose) {
- System.out.println("[SemanticAnalyzer] " + msg);
- }
+ public String getCurrentModule() {
+ return currentModuleName;
+ }
+
+ /**
+ * 设置当前正在分析的模块名。
+ *
+ * 用于限定结构体查找的优先作用域。
+ *
+ * @param moduleName 当前模块名
+ */
+ public void setCurrentModule(String moduleName) {
+ this.currentModuleName = moduleName;
}
- // ------------------ 工具函数 ------------------
+ // ==== 类型解析工具 ====
/**
- * 将类型名称字符串解析为对应的类型实例(支持多维数组后缀)。
+ * 解析类型字符串为 {@link Type} 实例。
*
- * 例如,"int" → int 类型,"int[][]" → 二维整型数组类型。
- *
+ * 支持内建类型、数组类型(带 "[]" 后缀)、用户自定义结构体类型。
+ * 类型解析的查找顺序为:
+ * 1. 内建类型;
+ * 2. 当前模块定义的结构体类型;
+ * 3. 当前模块导入模块中的结构体类型;
+ * 4. 全局所有模块的结构体类型。
*
- * @param name 类型名称(支持 "[]" 数组后缀)
- * @return 对应的 {@link Type} 实例,若无法识别返回 null
+ * @param typeName 类型名称字符串,如 "int"、"Foo"、"Bar[][]"
+ * @return 解析出的 {@link Type} 实例,若找不到则返回 null
*/
- public Type parseType(String name) {
- int dims = 0;
+ public Type parseType(String typeName) {
+ if (typeName == null) return null; // 处理空输入
+
+ String name = typeName; // 剥离数组后缀前的基本类型名
+ int dims = 0; // 记录数组维度
+ // 支持多维数组:如 "int[][]" -> dims=2
while (name.endsWith("[]")) {
name = name.substring(0, name.length() - 2);
dims++;
}
+
+ // 1) 优先查找内建类型
Type base = BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
- if (base == null) return null;
- Type t = base;
- for (int i = 0; i < dims; i++) {
- t = new ArrayType(t);
+
+ // 2) 如果不是内建类型,则尝试查找结构体类型
+ if (base == null) {
+ // 2.1 当前模块下的结构体
+ if (currentModuleName != null && modules.containsKey(currentModuleName)) {
+ ModuleInfo mi = modules.get(currentModuleName);
+ if (mi.getStructs().containsKey(name)) {
+ base = mi.getStructs().get(name);
+ } else {
+ // 2.2 当前模块导入的模块中的结构体
+ for (String imp : mi.getImports()) {
+ ModuleInfo im = modules.get(imp);
+ if (im != null && im.getStructs().containsKey(name)) {
+ base = im.getStructs().get(name);
+ break;
+ }
+ }
+ }
+ }
}
+
+ if (base == null) return null; // 所有路径均未找到
+
+ // 包装数组类型:根据数组维度递归封装
+ Type t = base;
+ for (int i = 0; i < dims; i++) t = new ArrayType(t);
return t;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java
index c57bd0757d365266dffebf994821fa8c9173239a..6107fedaa3363d8ceede66a590b9000f3a1d2786 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java
@@ -4,123 +4,110 @@ import org.jcnc.snow.compiler.parser.ast.DeclarationNode;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
import org.jcnc.snow.compiler.parser.ast.ReturnNode;
+import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.symbol.Symbol;
import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
+import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.ArrayList;
import java.util.List;
/**
- * {@code FunctionChecker} 是 Snow 编译器语义分析阶段用于检查所有函数体合法性的总控调度器。
+ * {@code FunctionChecker} 负责对所有模块的函数体进行两遍扫描式的语义检查。
*
- * 设计核心: 采用“两遍扫描”方案,彻底解决跨模块全局变量/常量类型推断和引用依赖问题:
+ * 检查流程为:
+ *
+ * 第一遍:为每个模块构建全局符号表,注册所有模块级变量与常量声明(并检查重复/未知类型)。
+ * 第二遍:在所有全局符号表准备好后,依次分析各模块下所有函数的参数和函数体语句。
+ *
+ * 检查要点:
*
- * 第一遍 :为所有模块预先构建并注册其全局符号表(globals),保证跨模块引用时可见。
- * 第二遍 :在全局符号表全部就绪后,依次分析所有模块的函数体,实现局部作用域、类型推断、语义校验等任务。
+ * 类型未知或变量/常量重复声明时,均收集为语义错误。
+ * 所有函数参数注册为局部变量。
+ * 所有函数体语句分派到对应 StatementAnalyzer 实例做分析。
+ * 非 void 返回类型的函数,必须有至少一条 return。
*
- * 功能职责:
- *
- * 遍历所有模块,先建立 globals,再遍历并检查所有函数体语句。
- * 为每个函数体构建完整符号表,并注册参数变量。
- * 分发每条语句到对应 {@link StatementAnalyzer} 进行类型检查和错误校验。
- * 自动检查非 void 函数 return 完备性。
- * 记录所有语义错误,便于前端高亮和诊断。
- *
- *
- * @param ctx 全局语义分析上下文,持有模块信息、符号表、错误收集等资源
*/
public record FunctionChecker(Context ctx) {
/**
- * 主入口:对所有模块的所有函数体进行语义检查(两遍扫描实现)。
- *
- * 第一遍 :为每个模块提前构建全局符号表(包含本模块所有全局变量和常量),
- * 并注册到 {@link ModuleInfo},确保跨模块引用时所有全局符号都已可用。
- *
- * 第二遍 :遍历所有模块的所有函数,对每个函数体:
- *
- * 构建局部作用域,父作用域为对应模块的 globals;
- * 注册参数变量;
- * 依次分发每条语句到对应 {@link StatementAnalyzer},进行类型和语义检查;
- * 自动校验非 void 函数 return 完备性;
- * 将所有发现的问题统一记录到 {@link SemanticError} 列表。
- *
+ * 对传入的所有模块做函数体的两遍扫描式语义检查。
*
- * @param mods 所有模块的 AST 根节点集合
+ * @param mods 所有待分析的模块 AST 节点集合
*/
public void check(Iterable mods) {
List moduleList = new ArrayList<>();
- // ---------- 第1遍:收集所有全局符号表 ----------
+
+ // ---------- 第一遍:构建并注册各模块全局符号表 ----------
for (ModuleNode mod : mods) {
+ ctx.setCurrentModule(mod.name()); // 标记当前模块
moduleList.add(mod);
- // 获取当前模块的元信息
ModuleInfo mi = ctx.modules().get(mod.name());
- // 创建本模块全局作用域(无父作用域)
- SymbolTable globalScope = new SymbolTable(null);
+ SymbolTable globalScope = new SymbolTable(null); // 模块级全局作用域
- // 注册所有全局变量/常量到符号表
+ // 处理所有全局变量/常量声明
for (DeclarationNode g : mod.globals()) {
- var t = ctx.parseType(g.getType());
- SymbolKind k = g.isConst() ? SymbolKind.CONSTANT : SymbolKind.VARIABLE;
+ Type t = ctx.parseType(g.getType()); // 解析声明类型
+ if (t == null) {
+ // 类型未知,记录错误,兜底为 int 类型,避免后续 NullPointer
+ ctx.errors().add(new SemanticError(g, "未知类型: " + g.getType()));
+ t = BuiltinType.INT;
+ }
+ SymbolKind kind = g.isConst() ? SymbolKind.CONSTANT : SymbolKind.VARIABLE;
String dupType = g.isConst() ? "常量" : "变量";
- // 检查重复声明
- if (!globalScope.define(new Symbol(g.getName(), t, k))) {
- ctx.errors().add(new SemanticError(
- g,
- dupType + "重复声明: " + g.getName()
- ));
+ // 注册符号表(防止重名)
+ if (!globalScope.define(new Symbol(g.getName(), t, kind))) {
+ ctx.errors().add(new SemanticError(g, dupType + "重复声明: " + g.getName()));
}
}
- // 注册到模块信息,供跨模块引用
+ // 将全局符号表挂载到模块信息对象
mi.setGlobals(globalScope);
}
- // ---------- 第2遍:遍历所有函数,分析函数体 ----------
+ // ---------- 第二遍:遍历各模块函数并分析函数体 ----------
for (ModuleNode mod : moduleList) {
+ ctx.setCurrentModule(mod.name());
ModuleInfo mi = ctx.modules().get(mod.name());
- SymbolTable globalScope = mi.getGlobals();
+ SymbolTable globalScope = mi.getGlobals(); // 全局作用域
for (FunctionNode fn : mod.functions()) {
- // 构建函数局部作用域,父作用域为 globalScope
+ // 构建函数的局部作用域(父作用域为模块全局)
SymbolTable locals = new SymbolTable(globalScope);
- // 注册函数参数为局部变量
- fn.parameters().forEach(p ->
- locals.define(new Symbol(
- p.name(),
- ctx.parseType(p.type()),
- SymbolKind.VARIABLE
- ))
- );
+ // 注册所有函数参数到局部作用域,类型未知时兜底为 int
+ fn.parameters().forEach(p -> {
+ Type t = ctx.parseType(p.type());
+ if (t == null) {
+ ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
+ t = BuiltinType.INT;
+ }
+ locals.define(new Symbol(p.name(), t, SymbolKind.VARIABLE));
+ });
- // 分析函数体内每条语句
- for (var stmt : fn.body()) {
- var analyzer = ctx.getRegistry().getStatementAnalyzer(stmt);
+ // 分析函数体所有语句
+ for (StatementNode stmt : fn.body()) {
+ StatementAnalyzer analyzer =
+ ctx.getRegistry().getStatementAnalyzer(stmt);
if (analyzer != null) {
+ // 传递语义分析器“实例”,避免类型擦除/反射调用
analyzer.analyze(ctx, mi, fn, locals, stmt);
} else {
- ctx.errors().add(new SemanticError(
- stmt,
- "不支持的语句类型: " + stmt
- ));
+ // 语句类型未支持,收集错误
+ ctx.errors().add(new SemanticError(stmt, "不支持的语句类型: " + stmt));
}
}
- // 检查非 void 函数是否至少包含一条 return 语句
- var returnType = ctx.parseType(fn.returnType());
- if (returnType != BuiltinType.VOID) {
- boolean hasReturn = fn.body().stream()
- .anyMatch(stmtNode -> stmtNode instanceof ReturnNode);
+ // 非 void 函数,要求必须含至少一条 return 语句
+ Type ret = ctx.parseType(fn.returnType());
+ if (ret != null && ret != BuiltinType.VOID) {
+ boolean hasReturn = fn.body().stream().anyMatch(s -> s instanceof ReturnNode);
if (!hasReturn) {
- ctx.errors().add(new SemanticError(
- fn,
- "非 void 函数必须包含至少一条 return 语句"
- ));
+ ctx.errors().add(new SemanticError(fn, "非 void 函数必须包含至少一条 return 语句"));
}
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java
index d71dd42bb5b82b3871889adc05c900c2c55d0109..7312415824a4124f655e9f28d672d44c67a1d5bf 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/ModuleInfo.java
@@ -2,6 +2,7 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
import org.jcnc.snow.compiler.semantic.type.FunctionType;
+import org.jcnc.snow.compiler.semantic.type.StructType;
import java.util.HashMap;
import java.util.HashSet;
@@ -9,106 +10,155 @@ import java.util.Map;
import java.util.Set;
/**
- * {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装。
- *
- * 用于在分析期间管理模块间依赖、函数签名查找、全局符号表等关键任务。
- * 每个模块对应一个唯一的 {@code ModuleInfo} 实例,贯穿整个语义分析流程。
+ * {@code ModuleInfo} 表示单个模块在语义分析阶段的所有元信息封装。
*
- *
包含信息:
+ *
+ * 每个模块对应一个唯一的 ModuleInfo 实例,在语义分析期间用于管理和查询:
*
* 模块名称(全局唯一标识);
- * 该模块导入的其他模块名集合(跨模块引用支持);
- * 该模块中定义的所有函数签名 {@code Map};
- * 模块级全局符号表 {@link SymbolTable}(常量 / 全局变量,支持跨模块类型推断)。
+ * 本模块导入的其他模块名集合(跨模块依赖支持);
+ * 该模块中所有函数签名(用于查找/重名检测/类型推断);
+ * 模块级全局符号表(常量、全局变量,支持类型推断和常量折叠);
+ * 本模块定义的结构体类型(struct)。
*
+ *
*
- * 典型用途:
+ *
主要用途:
*
- * 用于函数签名类型查找、重名检测、跨模块引用校验等;
- * 全局符号表为类型检查与后端 IR 常量折叠等模块级分析提供支撑。
+ * 跨模块语义分析、符号查找、类型推断、常量查找、函数/类型重名校验等。
+ * 所有对该模块的引用、类型检查、全局变量访问等均依赖本类提供的索引信息。
*
+ *
*/
public class ModuleInfo {
/**
- * 模块名称,作为全局唯一标识
+ * 模块名称,作为全局唯一标识符。
+ * 不可为空。通常为文件名或逻辑模块名。
*/
private final String name;
/**
- * 该模块显式导入的模块名集合(用于跨模块访问符号)
+ * 该模块显式导入的模块名集合。
+ *
+ * 仅存储本模块 import 的模块名(如 import foo;),
+ * 用于限制跨模块访问符号时的合法性校验。
+ *
+ *
+ * 注意:集合为内部引用,允许外部直接添加或删除导入关系。
+ *
*/
private final Set imports = new HashSet<>();
/**
- * 该模块中定义的函数名 → 函数类型映射
+ * 该模块中声明的所有函数签名映射。
+ *
+ * 键为函数名,值为函数类型。
+ * 支持跨模块函数引用、重名检测和类型推断。
+ *
*/
private final Map functions = new HashMap<>();
/**
- * 模块级全局符号表(常量 / 全局变量)
+ * 该模块中声明的所有结构体类型(struct)。
+ *
+ * 键为结构体名称,值为结构体类型。
+ * 用于跨模块/跨作用域结构体查找与类型检查。
+ *
+ */
+ private final Map structs = new HashMap<>();
+
+ /**
+ * 模块级全局符号表。
+ *
+ * 记录本模块声明的所有常量和全局变量。
+ * 支持类型推断、跨模块常量折叠、全局符号查找。
+ * 由 FunctionChecker 阶段注入。
+ *
*/
private SymbolTable globals;
/**
- * 构造模块信息对象。
+ * 构造函数:根据模块名初始化模块元信息。
*
- * @param name 模块名称,必须唯一且不可为空
+ * @param name 模块名称,要求全局唯一,且不可为空。
*/
public ModuleInfo(String name) {
this.name = name;
}
/**
- * 获取模块名称。
+ * 获取当前模块的全局唯一名称。
*
- * @return 当前模块的唯一名称
+ * @return 模块名字符串
*/
public String getName() {
return name;
}
/**
- * 获取该模块导入的模块名称集合。
+ * 获取本模块导入的所有模块名称集合。
+ *
*
- * 返回集合为内部数据的直接引用,调用方可通过 {@code add}/{@code remove} 方法动态维护导入信息。
+ * 注意:返回为内部集合引用,调用方可直接对集合进行 add/remove 操作维护导入依赖关系。
+ *
*
- * @return 可变集合,包含所有导入模块名
+ * @return 可变集合,包含所有导入模块名(如 "foo", "bar")
*/
public Set getImports() {
return imports;
}
/**
- * 获取模块中已声明的函数签名表。
+ * 获取模块内所有函数签名表。
+ *
*
- * 映射键为函数名,值为对应的 {@link FunctionType}。
- * 返回对象为内部引用,可用于添加、修改或删除函数定义。
+ * 键为函数名,值为函数类型。
+ * 返回对象为内部映射,可用于动态添加/修改/删除函数定义。
+ *
*
- * @return 模块内函数定义映射表
+ * @return 函数名 → 函数类型映射表
*/
public Map getFunctions() {
return functions;
}
/**
- * 获取模块的全局符号表(包含常量与全局变量)。
+ * 获取模块定义的所有结构体类型。
+ *
+ *
+ * 键为结构体名,值为结构体类型描述。
+ *
+ *
+ * @return 结构体名 → 结构体类型映射
+ */
+ public Map getStructs() {
+ return structs;
+ }
+
+ /**
+ * 获取当前模块的全局符号表(包含常量与全局变量)。
+ *
*
- * 该符号表由语义分析的 FunctionChecker 阶段构建完成并注入。
- * 提供跨模块类型检查、常量折叠等能力。
+ * 该符号表通常由 FunctionChecker 阶段在全局扫描时构建并注入。
+ * 提供后续类型检查、常量折叠、跨模块全局符号访问等能力。
+ *
*
- * @return 当前模块的全局符号表
+ * @return 本模块全局符号表
*/
public SymbolTable getGlobals() {
return globals;
}
/**
- * 设置模块的全局符号表。
+ * 设置模块的全局符号表(仅限 FunctionChecker 构建时注入)。
+ *
*
- * 仅应由 FunctionChecker 在语义分析全局扫描阶段调用。
+ * 通常仅语义分析的全局阶段由框架内部调用,
+ * 不建议外部用户主动修改此符号表。
+ *
*
- * @param globals 全局符号表实例
+ * @param globals 新的全局符号表实例
*/
public void setGlobals(SymbolTable globals) {
this.globals = globals;
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java
index e5426468e03a2517f7dd8e4e0477d4055cb19223..71e6cb4ef5f70b9908d34de43220f68622c8da62 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/SignatureRegistrar.java
@@ -1,12 +1,10 @@
package org.jcnc.snow.compiler.semantic.core;
-import org.jcnc.snow.compiler.parser.ast.FunctionNode;
-import org.jcnc.snow.compiler.parser.ast.ImportNode;
-import org.jcnc.snow.compiler.parser.ast.ModuleNode;
-import org.jcnc.snow.compiler.parser.ast.ParameterNode;
+import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
import org.jcnc.snow.compiler.semantic.type.FunctionType;
+import org.jcnc.snow.compiler.semantic.type.StructType;
import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.ArrayList;
@@ -14,73 +12,170 @@ import java.util.List;
import java.util.Optional;
/**
- * {@code SignatureRegistrar} 负责函数签名登记与导入语义检查。
+ * {@code SignatureRegistrar}
*
- * 在语义分析初期阶段,它遍历每个模块,完成以下任务:
+ * 语义分析准备阶段:负责函数签名登记、结构体签名登记与 import 校验。
+ *
*
- * 验证所有 {@link ImportNode} 导入的模块是否存在于全局模块表 {@link Context#modules()} 中;
- * 将每个 {@link FunctionNode} 的函数签名(参数类型和返回类型)注册到对应 {@link ModuleInfo} 中;
- * 在参数或返回类型无法识别时,记录 {@link SemanticError},并进行容错降级。
+ * 验证每个模块声明的 import 模块在全局模块表 {@link Context#modules()} 是否存在。
+ * 将每个函数、结构体方法、构造函数的类型签名登记到 {@link ModuleInfo},便于后续类型推断。
+ * 支持 {@code extends} 单继承:子类会继承父类的字段与方法。
+ * 若参数或返回类型无法解析,则报错并降级为 int 或 void,保证语义分析流程健壮。
*
- * 本组件作为语义分析的准备阶段,为后续函数体检查提供函数类型上下文。
- *
- * @param ctx 全局语义分析上下文,提供模块、类型、错误管理等功能
+ *
+ * 作为语义分析前置流程,为后续函数体和表达式分析提供类型环境。
*/
public record SignatureRegistrar(Context ctx) {
/**
- * 构造函数签名注册器。
+ * 遍历所有模块,注册函数/方法/结构体签名,校验 import 合法性。
*
- * @param ctx 当前语义分析上下文
+ * @param modules 需要分析的所有模块列表(AST 顶层节点)
*/
- public SignatureRegistrar {
- }
-
- /**
- * 遍历模块并注册函数签名,同时校验导入模块的合法性。
- *
- * @param mods 所有模块的语法树节点集合
- */
- public void register(Iterable mods) {
- for (ModuleNode mod : mods) {
+ public void register(Iterable modules) {
+ for (ModuleNode mod : modules) {
+ ctx.setCurrentModule(mod.name()); // 切换上下文到当前模块
ModuleInfo mi = ctx.modules().get(mod.name());
- // ---------- 1. 模块导入检查 ----------
+ // ========== 1) 校验 imports ==========
for (ImportNode imp : mod.imports()) {
if (!ctx.modules().containsKey(imp.moduleName())) {
- ctx.errors().add(new SemanticError(
- imp,
- "未知模块: " + imp.moduleName()
- ));
+ // 导入的模块在全局表中不存在,报错
+ ctx.errors().add(new SemanticError(imp, "未知模块: " + imp.moduleName()));
} else {
+ // 添加到本模块导入集合
mi.getImports().add(imp.moduleName());
}
}
- // ---------- 2. 函数签名注册 ----------
+ // ========== 2) 结构体签名登记 ==========
+ for (StructNode stn : mod.structs()) {
+ // 构造结构体类型对象,唯一标识为 (模块名, 结构体名)
+ StructType st = new StructType(mod.name(), stn.name());
+ mi.getStructs().put(stn.name(), st);
+
+ // --- 2.0 字段签名登记 ---
+ if (stn.fields() != null) {
+ for (DeclarationNode field : stn.fields()) {
+ Type ft = ctx.parseType(field.getType());
+ if (ft == null) {
+ ctx.errors().add(new SemanticError(field, "未知类型: " + field.getType()));
+ ft = BuiltinType.INT;
+ }
+ st.getFields().put(field.getName(), ft);
+ }
+ }
+
+ // --- 2.1 多个构造函数 init(重载,按参数个数区分) ---
+ if (stn.inits() != null) {
+ for (FunctionNode initFn : stn.inits()) {
+ List ptypes = new ArrayList<>();
+ for (ParameterNode p : initFn.parameters()) {
+ // 解析参数类型,不存在则报错降级为 int
+ Type t = ctx.parseType(p.type());
+ if (t == null) {
+ ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
+ t = BuiltinType.INT;
+ }
+ ptypes.add(t);
+ }
+ // 构造函数返回类型固定为 void
+ st.addConstructor(new FunctionType(ptypes, BuiltinType.VOID));
+ }
+ }
+
+ // --- 2.2 方法签名 ---
+ for (FunctionNode fn : stn.methods()) {
+ List ptypes = new ArrayList<>();
+ for (ParameterNode p : fn.parameters()) {
+ Type t = ctx.parseType(p.type());
+ if (t == null) {
+ ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
+ t = BuiltinType.INT;
+ }
+ ptypes.add(t);
+ }
+ Type ret = Optional.ofNullable(ctx.parseType(fn.returnType()))
+ .orElse(BuiltinType.VOID);
+ st.getMethods().put(fn.name(), new FunctionType(ptypes, ret));
+ }
+ }
+
+ // ========== 2.3 继承处理 ==========
+ for (StructNode stn : mod.structs()) {
+ if (stn.parent() != null) {
+ StructType child = mi.getStructs().get(stn.name());
+ StructType parent = resolveParentStruct(mi, stn.parent());
+
+ if (parent == null) {
+ // 父类不存在(既不在本模块,也不在导入模块 / 限定名错误),报语义错误
+ ctx.errors().add(new SemanticError(stn, "未知父类: " + stn.parent()));
+ } else {
+ // 建立继承链
+ child.setParent(parent);
+
+ // 继承字段
+ parent.getFields().forEach(
+ (k, v) -> child.getFields().putIfAbsent(k, v));
+ // 继承方法
+ parent.getMethods().forEach(
+ (k, v) -> child.getMethods().putIfAbsent(k, v));
+ // 构造函数不继承
+ }
+ }
+ }
+
+ // ========== 3) 模块级函数签名登记 ==========
for (FunctionNode fn : mod.functions()) {
List params = new ArrayList<>();
-
- // 参数类型解析
for (ParameterNode p : fn.parameters()) {
- Type t = Optional.ofNullable(ctx.parseType(p.type()))
- .orElseGet(() -> {
- ctx.errors().add(new SemanticError(
- p,
- "未知类型: " + p.type()
- ));
- return BuiltinType.INT; // 容错降级
- });
+ Type t = ctx.parseType(p.type());
+ if (t == null) {
+ ctx.errors().add(new SemanticError(p, "未知类型: " + p.type()));
+ t = BuiltinType.INT;
+ }
params.add(t);
}
-
- // 返回类型解析(默认降级为 void)
Type ret = Optional.ofNullable(ctx.parseType(fn.returnType()))
.orElse(BuiltinType.VOID);
-
- // 注册函数签名
mi.getFunctions().put(fn.name(), new FunctionType(params, ret));
}
}
}
+
+ /**
+ * 解析父类结构体:
+ *
+ * 支持限定名 {@code Module.Struct}。
+ * 支持在本模块与 import 的模块中按未限定名查找。
+ *
+ */
+ private StructType resolveParentStruct(ModuleInfo mi, String parentName) {
+ // 1. 限定名:Module.Struct
+ int dot = parentName.indexOf('.');
+ if (dot > 0 && dot < parentName.length() - 1) {
+ String m = parentName.substring(0, dot);
+ String s = parentName.substring(dot + 1);
+ ModuleInfo pm = ctx.modules().get(m);
+ if (pm != null) {
+ return pm.getStructs().get(s);
+ }
+ return null;
+ }
+
+ // 2. 先在当前模块找
+ StructType local = mi.getStructs().get(parentName);
+ if (local != null) return local;
+
+ // 3. 在导入模块中找
+ for (String imported : mi.getImports()) {
+ ModuleInfo pim = ctx.modules().get(imported);
+ if (pim == null) continue;
+ StructType st = pim.getStructs().get(parentName);
+ if (st != null) return st;
+ }
+
+ // 未找到
+ return null;
+ }
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/StructType.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/StructType.java
new file mode 100644
index 0000000000000000000000000000000000000000..80c2cdead407f92cfc39ab71a86ec80ed953d225
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/StructType.java
@@ -0,0 +1,232 @@
+package org.jcnc.snow.compiler.semantic.type;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * {@code StructType}
+ *
+ * 表示用户自定义结构体类型,用于类型系统中描述结构体实例的静态类型信息。
+ *
+ * 由 (moduleName, structName) 唯一标识(支持跨模块同名 struct)。
+ * 包含字段定义、方法签名、构造函数等全部类型元信息。
+ * 支持父类引用,用于实现继承与运行时多态。
+ *
+ *
+ */
+public class StructType implements Type {
+ /**
+ * 所属模块名称,用于唯一标识、支持跨模块同名结构体
+ */
+ private final String moduleName;
+
+ /**
+ * 结构体类型名称(如 "Person")
+ */
+ private final String name;
+
+ /**
+ * 构造函数签名表:参数个数 → 构造函数类型
+ */
+ private final Map constructors = new HashMap<>();
+
+ /**
+ * 字段定义表:字段名 → 字段类型
+ */
+ private final Map fields = new HashMap<>();
+
+ /**
+ * 方法签名表:方法名 → 函数类型
+ */
+ private final Map methods = new HashMap<>();
+
+ /**
+ * 父类类型(可为 null,表示没有继承)
+ */
+ private StructType parent;
+
+ /**
+ * 默认构造函数(可能为 null,表示无参构造)
+ */
+ private FunctionType constructor;
+
+ /**
+ * 构造函数:创建结构体类型描述。
+ *
+ * @param moduleName 所属模块名(不可为 {@code null})
+ * @param name 结构体名称(不可为 {@code null})
+ */
+ public StructType(String moduleName, String name) {
+ this.moduleName = moduleName;
+ this.name = name;
+ }
+
+ /**
+ * 获取结构体所属模块名。
+ *
+ * @return 模块名称
+ */
+ public String moduleName() {
+ return moduleName;
+ }
+
+ /**
+ * 获取父类类型。
+ *
+ * @return 父类 {@link StructType},若无继承则返回 {@code null}
+ */
+ public StructType getParent() {
+ return parent;
+ }
+
+ /**
+ * 设置父类类型。
+ *
+ * @param parent 父类 {@link StructType}
+ */
+ public void setParent(StructType parent) {
+ this.parent = parent;
+ }
+
+ /* ---------------- 构造函数相关 ---------------- */
+
+ /**
+ * 添加一个构造函数签名。
+ *
+ * 构造函数按参数个数唯一索引,同参数个数的构造函数将被覆盖。
+ *
+ *
+ * @param ctor 构造函数的 {@link FunctionType} 实例
+ */
+ public void addConstructor(FunctionType ctor) {
+ constructors.put(ctor.paramTypes().size(), ctor);
+ }
+
+ /**
+ * 根据参数个数获取对应构造函数。
+ *
+ * @param argc 参数个数
+ * @return 对应的构造函数签名,若不存在则返回 {@code null}
+ */
+ public FunctionType getConstructor(int argc) {
+ return constructors.get(argc);
+ }
+
+ /**
+ * 获取所有构造函数签名(不可变视图)。
+ *
+ * @return 形参个数 → 构造函数签名的映射
+ */
+ public Map getConstructors() {
+ return Map.copyOf(constructors);
+ }
+
+ /**
+ * 获取结构体名称。
+ *
+ * @return 结构体类型名
+ */
+ @Override
+ public String name() {
+ return name;
+ }
+
+ /**
+ * 获取默认构造函数。
+ *
+ * @return 默认构造函数类型,若不存在则返回 {@code null}
+ */
+ public FunctionType getConstructor() {
+ return constructor;
+ }
+
+ /**
+ * 设置默认构造函数。
+ *
+ * @param ctor 构造函数类型
+ */
+ public void setConstructor(FunctionType ctor) {
+ this.constructor = ctor;
+ }
+
+ /**
+ * 获取所有字段定义。
+ *
+ * @return 字段定义表:字段名 → 字段类型
+ */
+ public Map getFields() {
+ return fields;
+ }
+
+ /**
+ * 获取所有方法签名。
+ *
+ * @return 方法签名表:方法名 → 函数类型
+ */
+ public Map getMethods() {
+ return methods;
+ }
+
+ /* ---------------- 类型兼容性(支持继承链) ---------------- */
+
+ /**
+ * 判断类型兼容性。
+ *
+ * 若模块名 + 结构体名相等,则兼容。
+ * 若 {@code other} 是当前类型的子类,也兼容(支持父类引用)。
+ *
+ *
+ * @param other 另一个类型
+ * @return 若兼容返回 {@code true},否则 {@code false}
+ */
+ @Override
+ public boolean isCompatible(Type other) {
+ if (!(other instanceof StructType s)) return false;
+ // 沿着继承链查找
+ for (StructType cur = s; cur != null; cur = cur.parent) {
+ if (this.equals(cur)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * 判断类型相等。
+ *
+ * 比较规则:模块名 + 结构体名全等。
+ *
+ *
+ * @param o 另一个对象
+ * @return 相等返回 {@code true},否则 {@code false}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof StructType s)) return false;
+ return Objects.equals(name, s.name) &&
+ Objects.equals(moduleName, s.moduleName);
+ }
+
+ /**
+ * 计算哈希值,与 {@link #equals(Object)} 保持一致。
+ *
+ * 用于保证在 {@link Map} / {@link java.util.Set} 中索引正确。
+ *
+ *
+ * @return 哈希码
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(moduleName, name);
+ }
+
+ /**
+ * 返回字符串表示(调试用)。
+ *
+ * @return 结构体类型名字符串
+ */
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
index 8eba5b96db64289c8b4a35d36fbd895b4e5c136d..be28eee863d55070ae1c85276a2168a35330e8a1 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/CompileTask.java
@@ -31,71 +31,61 @@ import java.util.*;
import static org.jcnc.snow.common.SnowConfig.print;
/**
- * CLI 任务: 编译 .snow 源文件为 VM 字节码(.water 文件)。
+ * CompileTask: 将 .snow 源文件编译为 VM 字节码文件 (.water) 的 CLI 任务实现。
*
- * 支持单文件、多文件和目录递归编译,并可在编译后立即运行虚拟机。
- * 命令行参数支持 run、-o、-d 及直接指定源文件。
- *
- *
+ * 用法示例:
*
- * 用法示例:
- * $ snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow ...]
+ * snow compile [run] [-o <name>] [-d <srcDir>] [file1.snow file2.snow ...]
*
+ * 支持编译完成后立即运行生成的 VM。
*/
-public final class CompileTask implements Task {
- /**
- * 项目信息
- */
- private final Project project;
- /**
- * 原始命令行参数
- */
- private final String[] args;
+public record CompileTask(Project project, String[] args) implements Task {
- /**
- * 创建一个编译任务。
- *
- * @param project 项目信息对象
- * @param args 命令行参数数组
- */
- public CompileTask(Project project, String[] args) {
- this.project = project;
- this.args = args;
- }
+ /* ------------------------------------------------------------------ */
+ /* 1. 构造方法和成员变量 */
+ /* ------------------------------------------------------------------ */
/**
- * 创建一个不带参数的编译任务。
+ * 使用默认空参数数组的构造方法。
*
- * @param project 项目信息对象
+ * @param project 当前项目对象
*/
public CompileTask(Project project) {
this(project, new String[0]);
}
- /* ---------------------- 调试输出工具 ---------------------- */
+ /* ------------------------------------------------------------------ */
+ /* 2. 工具方法 */
+ /* ------------------------------------------------------------------ */
/**
- * 推断 .water 输出文件名。
+ * 推断输出 .water 文件的路径。
+ *
+ * 规则:
*
- * 如果指定 -o,直接使用该名称。
- * 目录编译时,取目录名。
- * 单文件编译时,取文件名去掉 .snow 后缀。
- * 否则默认 "program"。
+ * 若指定了 outName,则以 outName 为基名
+ * 否则若指定了目录,则以目录名为基名
+ * 否则若只有一个源文件,则以该文件名(去 .snow 后缀)为基名
+ * 否则默认为 "program"
*
*
- * @param sources 源文件路径列表
- * @param outName 输出文件名(如有指定,否则为 null)
- * @param dir 源码目录(如有指定,否则为 null)
- * @return 推断出的输出文件路径(.water 文件)
+ * @param sources 源文件列表
+ * @param outName 用户指定的输出文件基名
+ * @param dir 用户指定的源目录
+ * @return 推断得到的 .water 文件路径
*/
- private static Path deriveOutputPath(List sources, String outName, Path dir) {
+ private static Path deriveOutputPath(List sources,
+ String outName,
+ Path dir) {
String base;
if (outName != null) {
base = outName;
} else if (dir != null) {
base = dir.getFileName().toString();
} else if (sources.size() == 1) {
- base = sources.getFirst().getFileName().toString()
+ base = sources.getFirst()
+ .getFileName()
+ .toString()
.replaceFirst("\\.snow$", "");
} else {
base = "program";
@@ -104,15 +94,16 @@ public final class CompileTask implements Task {
}
/**
- * 将 main 函数调整至函数列表首位,确保程序入口为 PC=0。
+ * 将 main 或 *.main 函数调整到列表首位,确保程序入口的 PC = 0。
*
* @param in 原始 IRProgram
- * @return 调整入口后的 IRProgram
+ * @return 调整入口顺序后的 IRProgram
*/
private static IRProgram reorderForEntry(IRProgram in) {
List ordered = new ArrayList<>(in.functions());
for (int i = 0; i < ordered.size(); i++) {
- if ("main".equals(ordered.get(i).name())) {
+ String fn = ordered.get(i).name();
+ if ("main".equals(fn) || fn.endsWith(".main")) {
Collections.swap(ordered, 0, i);
break;
}
@@ -122,10 +113,14 @@ public final class CompileTask implements Task {
return out;
}
+ /* ------------------------------------------------------------------ */
+ /* 3. 任务执行入口 */
+ /* ------------------------------------------------------------------ */
+
/**
- * 执行编译任务。该方法会解析参数并调用 {@link #execute(String[])} 进行实际编译流程。
+ * 执行任务入口,调用 execute 并处理异常。
*
- * @throws Exception 执行过程中出现任意异常时抛出
+ * @throws Exception 若执行过程出现异常
*/
@Override
public void run() throws Exception {
@@ -144,15 +139,15 @@ public final class CompileTask implements Task {
* @throws Exception 编译或写入过程中出现异常时抛出
*/
public int execute(String[] args) throws Exception {
- // ---------------- 解析命令行参数 ----------------
+
+ /* ---------------- 3.1 解析命令行参数 ---------------- */
boolean runAfterCompile = false;
String outputName = null;
Path dir = null;
List sources = new ArrayList<>();
for (int i = 0; i < args.length; i++) {
- String arg = args[i];
- switch (arg) {
+ switch (args[i]) {
case "run" -> runAfterCompile = true;
case "--debug" -> SnowConfig.MODE = Mode.DEBUG;
case "-o" -> {
@@ -172,10 +167,10 @@ public final class CompileTask implements Task {
}
}
default -> {
- if (arg.endsWith(".snow")) {
- sources.add(Path.of(arg));
+ if (args[i].endsWith(".snow")) {
+ sources.add(Path.of(args[i]));
} else {
- System.err.println("Unknown option or file: " + arg);
+ System.err.println("Unknown option or file: " + args[i]);
new CompileCommand().printUsage();
return 1;
}
@@ -183,7 +178,7 @@ public final class CompileTask implements Task {
}
}
- // --------- 如果指定了目录则递归收集所有 *.snow ---------
+ /* ---------------- 3.2 递归收集目录中的 .snow 文件 ---------------- */
if (dir != null) {
if (!Files.isDirectory(dir)) {
System.err.println("Not a directory: " + dir);
@@ -191,7 +186,7 @@ public final class CompileTask implements Task {
}
try (var stream = Files.walk(dir)) {
stream.filter(p -> p.toString().endsWith(".snow"))
- .sorted() // 确保稳定顺序
+ .sorted()
.forEach(sources::add);
}
}
@@ -200,87 +195,73 @@ public final class CompileTask implements Task {
System.err.println("No .snow source files found.");
return 1;
}
-
- // 多文件但未指定 -o 且非目录编译 —— 提示必须指定输出名
if (sources.size() > 1 && outputName == null && dir == null) {
System.err.println("Please specify output name using -o ");
return 1;
}
- // ---------------- 1. 词法/语法分析,并打印源代码 ----------------
+ /* ---------------- 4. 词法和语法分析 ---------------- */
List allAst = new ArrayList<>();
print("## 编译器输出");
print("### Snow 源代码");
- for (Path p : sources) {
- if (!Files.exists(p)) {
- System.err.println("File not found: " + p);
- return 1;
- }
-
- String code = Files.readString(p, StandardCharsets.UTF_8);
-
- // 打印源码
- print("#### " + p.getFileName());
+ for (Path src : sources) {
+ String code = Files.readString(src, StandardCharsets.UTF_8);
+ print("#### " + src.getFileName());
print(code);
- // 词法、语法分析
- LexerEngine lexer = new LexerEngine(code, p.toString());
- // 若词法阶段存在错误,立即终止编译,避免进入后续的语法及语义分析
- if (!lexer.getErrors().isEmpty()) {
- return 1;
- }
+ LexerEngine lex = new LexerEngine(code, src.toString());
+ if (!lex.getErrors().isEmpty()) return 1;
- ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
+ ParserContext ctx = new ParserContext(lex.getAllTokens(), src.toString());
allAst.addAll(new ParserEngine(ctx).parse());
}
- // ---------------- 2. 语义分析 ----------------
+ /* ---------------- 5. 语义分析 ---------------- */
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
- // ---------------- 3. AST → IR,并将 main 函数移动至首位 ----------------
+ /* ---------------- 6. AST 转 IR,并调整入口 ---------------- */
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
- program = reorderForEntry(program);
+ program = reorderForEntry(program); // 确保 main 在首位
- // 打印 AST 和 IR
- print("### AST");
- if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst);
+// print("### AST");
+// if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst);
print("### IR");
print(program.toString());
- // ---------------- 4. IR → VM 指令 ----------------
+ /* ---------------- 7. IR 转 VM 指令 ---------------- */
VMProgramBuilder builder = new VMProgramBuilder();
- List> generators =
+ List> gens =
InstructionGeneratorProvider.defaultGenerators();
for (IRFunction fn : program.functions()) {
Map slotMap =
new RegisterAllocator().allocate(fn);
- new VMCodeGenerator(slotMap, builder, generators).generate(fn);
+ new VMCodeGenerator(slotMap, builder, gens).generate(fn);
}
- List finalCode = builder.build();
+ List vmCode = builder.build();
print("### VM code");
if (SnowConfig.isDebug()) {
- for (int i = 0; i < finalCode.size(); i++) {
- String[] parts = finalCode.get(i).split(" ");
+ for (int i = 0; i < vmCode.size(); i++) {
+ String[] parts = vmCode.get(i).split(" ");
String name = OpHelper.opcodeName(parts[0]);
parts = Arrays.copyOfRange(parts, 1, parts.length);
print("%04d: %-10s %s%n", i, name, String.join(" ", parts));
}
}
- // ---------------- 5. 写出 .water 文件 ----------------
- Path outputFile = deriveOutputPath(sources, outputName, dir);
- Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
- print("Written to " + outputFile.toAbsolutePath());
+ /* ---------------- 8. 写出 .water 文件 ---------------- */
+ Path outFile = deriveOutputPath(sources, outputName, dir);
+ Files.write(outFile, vmCode, StandardCharsets.UTF_8);
+ print("Written to " + outFile.toAbsolutePath());
- // ---------------- 6. 立即运行 VM ----------------
+ /* ---------------- 9. 可选运行 VM ---------------- */
if (runAfterCompile) {
print("\n=== Launching VM ===");
- VMLauncher.main(new String[]{outputFile.toString()});
+ VMLauncher.main(new String[]{outFile.toString()});
print("\n=== VM exited ===");
}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
index 90741be5ca6b289c8ce5d8b5bd9257dba569a070..c46571cb4467d52b3ba1b3fdd2461fcb3a457c23 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
@@ -57,29 +57,40 @@ public class CallCommand implements Command {
CallStack callStack) {
if (parts.length < 3)
- throw new IllegalArgumentException("CALL: need ");
+ throw new IllegalArgumentException("CALL: need ");
- int targetAddr;
- int nArgs;
- try {
- targetAddr = Integer.parseInt(parts[1]);
- nArgs = Integer.parseInt(parts[2]);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("CALL: malformed operands", e);
- }
+ /* ----------- Parse target address / method signature ----------- */
+ final String targetToken = parts[1]; // Can be a numeric address or "@Class::method"
+ final int nArgs = Integer.parseInt(parts[2]);
- /* build new frame & local table for callee */
+ /* ----------- Build callee's local variable store ----------- */
LocalVariableStore calleeLVS = new LocalVariableStore();
-
- /* transfer arguments: operand stack top is last arg */
for (int slot = nArgs - 1; slot >= 0; slot--) {
if (operandStack.isEmpty())
throw new IllegalStateException("CALL: operand stack underflow");
calleeLVS.setVariable(slot, operandStack.pop());
}
- StackFrame newFrame = new StackFrame(currentPC + 1, calleeLVS,
- new MethodContext("subroutine@" + targetAddr, null));
+ /* ----------- Handle virtual call ----------- */
+ int targetAddr;
+ if (targetToken.startsWith("@")) { // "@Class::method" convention indicates a virtual call
+ Object thisRef = calleeLVS.getVariable(0); // By convention, slot-0 stores "this"
+ if (!(thisRef instanceof Instance inst))
+ throw new IllegalStateException("VCALL: slot-0 is not an object reference");
+
+ String methodSig = targetToken.substring(1); // Remove '@'
+ targetAddr = inst.vtable().lookup(methodSig);
+ } else {
+ /* Direct/static call (numeric address) */
+ targetAddr = Integer.parseInt(targetToken);
+ }
+
+ /* ----------- Push new stack frame and jump ----------- */
+ StackFrame newFrame = new StackFrame(
+ currentPC + 1,
+ calleeLVS,
+ new MethodContext("subroutine@" + targetAddr, null)
+ );
callStack.pushFrame(newFrame);
print("\nCalling function at address: " + targetAddr);
diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
index ef73aed538e8797ca22f699cd7fbdfe5aabbbd96..228f957dbdf9482d7dca0f479b562a0b3426e153 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
@@ -35,9 +35,6 @@ import java.util.List;
* R_PUSH [1, 2, 3] // pushes ArrayList {1, 2, 3}
* R_PUSH [1, [2, 3], 4] // pushes nested arrays as mutable lists
*
- *
- * @author (your name or org)
- * @since 1.0
*/
public class RPushCommand implements Command {
diff --git a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
index 80428fa13f4c2b982f595a94a820aee37b1f175e..2bdd5fec7bf99fdcc8eb1dfb8088412b4a31ba83 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
@@ -400,9 +400,20 @@ public class SyscallCommand implements Command {
// 必须是可变 List
@SuppressWarnings("unchecked")
java.util.List mlist = (java.util.List) list;
- if (idx < 0 || idx >= mlist.size())
+ if (idx < 0) {
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + mlist.size() + ")");
- mlist.set(idx, value);
+ }
+ // 允许自动扩容:当 idx == size 时等价于 append;当 idx > size 时以 null 填充到 idx-1,再 set(idx, value)
+ if (idx >= mlist.size()) {
+ // 以 null 填充直到 size == idx
+ while (mlist.size() < idx) {
+ mlist.add(null);
+ }
+ // 此时 size == idx,直接追加
+ mlist.add(value);
+ } else {
+ mlist.set(idx, value);
+ }
} else if (arrObj != null && arrObj.getClass().isArray()) {
int len = java.lang.reflect.Array.getLength(arrObj);
if (idx < 0 || idx >= len)
diff --git a/src/main/java/org/jcnc/snow/vm/execution/CommandExecutionHandler.java b/src/main/java/org/jcnc/snow/vm/execution/CommandExecutionHandler.java
index 470f7bcc10ad8605a96911ffda3d84fc58a95f86..a2ab2dae06e83070b3e6604b5550f0c64bfb9c36 100644
--- a/src/main/java/org/jcnc/snow/vm/execution/CommandExecutionHandler.java
+++ b/src/main/java/org/jcnc/snow/vm/execution/CommandExecutionHandler.java
@@ -54,7 +54,7 @@ public record CommandExecutionHandler(OperandStack operandStack, LocalVariableSt
} catch (Exception e) {
System.err.println("Command execution error (PC=" + currentPC + ") -> "
+ e.getMessage());
- return -1; // 让 VM 主循环安全终止
+ return -1; // Ensure the VM main loop terminates safely
}
}
}
diff --git a/src/main/java/org/jcnc/snow/vm/io/FDTable.java b/src/main/java/org/jcnc/snow/vm/io/FDTable.java
index 98d6385473b0518a755d1e7f886106f38fa06d5d..0cd40b0e3ffdd6a25c1986a8f473bb20556076cf 100644
--- a/src/main/java/org/jcnc/snow/vm/io/FDTable.java
+++ b/src/main/java/org/jcnc/snow/vm/io/FDTable.java
@@ -8,55 +8,72 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * 维护 “虚拟 fd → Java NIO Channel” 的全局映射表。
+ * Maintains a global mapping table: “virtual fd → Java NIO Channel”.
*
*
* 0 → stdin (ReadableByteChannel)
* 1 → stdout (WritableByteChannel)
* 2 → stderr (WritableByteChannel)
- * 3+ → 运行期动态分配
+ * 3+ → Dynamically allocated at runtime
*
*/
public final class FDTable {
private FDTable() {}
- /** 下一次可用 fd(0‒2 保留给标准流) */
+ /** Next available fd (0‒2 are reserved for standard streams) */
private static final AtomicInteger NEXT_FD = new AtomicInteger(3);
- /** 主映射表:fd → Channel */
+ /** Main mapping table: fd → Channel */
private static final ConcurrentHashMap MAP = new ConcurrentHashMap<>();
static {
- // JVM 标准流包装成 NIO Channel 后放入表中
+ // Wrap JVM standard streams as NIO Channels and put them into the table
MAP.put(0, Channels.newChannel(new BufferedInputStream(System.in)));
MAP.put(1, Channels.newChannel(new BufferedOutputStream(System.out)));
MAP.put(2, Channels.newChannel(new BufferedOutputStream(System.err)));
}
- /** 注册新 Channel,返回分配到的虚拟 fd */
+ /**
+ * Register a new Channel, returning the allocated virtual fd.
+ * @param ch Channel to register
+ * @return allocated fd
+ */
public static int register(Channel ch) {
int fd = NEXT_FD.getAndIncrement();
MAP.put(fd, ch);
return fd;
}
- /** 取得 Channel;如果 fd 不存在则返回 null */
+ /**
+ * Retrieve the Channel by fd; returns null if fd does not exist.
+ * @param fd file descriptor
+ * @return Channel or null if not found
+ */
public static Channel get(int fd) {
return MAP.get(fd);
}
- /** 关闭并移除 fd(0‒2 忽略) */
+ /**
+ * Close and remove the fd (0‒2 are ignored).
+ * @param fd file descriptor to close
+ * @throws IOException if an I/O error occurs
+ */
public static void close(int fd) throws IOException {
- if (fd <= 2) return; // 标准流交由宿主 JVM 维护
+ if (fd <= 2) return; // Standard streams are managed by the host JVM
Channel ch = MAP.remove(fd);
if (ch != null && ch.isOpen()) ch.close();
}
- /** 类似 dup(oldfd) —— 返回指向同一 Channel 的新 fd */
+ /**
+ * Similar to dup(oldfd) — returns a new fd referring to the same Channel.
+ * @param oldfd old file descriptor to duplicate
+ * @return new fd referring to the same Channel
+ * @throws IllegalArgumentException if fd does not exist
+ */
public static int dup(int oldfd) {
Channel ch = MAP.get(oldfd);
if (ch == null)
throw new IllegalArgumentException("Bad fd: " + oldfd);
- return register(ch); // 多个 fd 引用同一 Channel
+ return register(ch); // Multiple fds refer to the same Channel
}
}
diff --git a/src/main/java/org/jcnc/snow/vm/module/Instance.java b/src/main/java/org/jcnc/snow/vm/module/Instance.java
new file mode 100644
index 0000000000000000000000000000000000000000..04c94513728c3cb478c2a2a28f1cbc99aaa88943
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/module/Instance.java
@@ -0,0 +1,91 @@
+package org.jcnc.snow.vm.module;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The {@code Instance} class represents a runtime object instance
+ * in the virtual machine.
+ *
+ * Each instance maintains:
+ *
+ * A reference to its {@link VirtualTable} (vtable), which provides
+ * dynamic method dispatch and runtime method lookup.
+ * A map of field values, where each field is identified by name
+ * and can hold an arbitrary object reference.
+ *
+ *
+ * Key responsibilities:
+ *
+ * Encapsulate the runtime state of an object.
+ * Enable field storage and retrieval through {@link #setField(String, Object)}
+ * and {@link #getField(String)}.
+ * Provide access to the instance’s virtual table for method resolution.
+ *
+ */
+public final class Instance {
+
+ /** The virtual table associated with this instance (immutable). */
+ private final VirtualTable vtable;
+
+ /** A mapping of field names to their runtime values. */
+ private final Map fields = new HashMap<>();
+
+ /**
+ * Constructs a new {@code Instance} with the specified virtual table.
+ *
+ * @param vtable The virtual table that defines the method dispatch behavior
+ * for this instance. Must not be {@code null}.
+ */
+ public Instance(VirtualTable vtable) {
+ this.vtable = vtable;
+ }
+
+ /* ---------- Virtual Table ---------- */
+
+ /**
+ * Returns the virtual table associated with this instance.
+ *
+ * The virtual table is used for runtime method resolution
+ * (dynamic dispatch).
+ *
+ *
+ * @return The {@link VirtualTable} of this instance.
+ */
+ public VirtualTable vtable() {
+ return vtable;
+ }
+
+ /* ---------- Fields ---------- */
+
+ /**
+ * Sets the value of a field in this instance.
+ *
+ * If the field does not already exist, it is created.
+ * If the field exists, its value is overwritten.
+ *
+ *
+ * @param name The name of the field to set. Must not be {@code null}.
+ * @param value The value to assign to the field. Can be {@code null}.
+ */
+ public void setField(String name, Object value) {
+ fields.put(name, value);
+ }
+
+ /**
+ * Retrieves the value of a field in this instance.
+ *
+ * The value is cast to the expected type at runtime.
+ * If the field is not present, this method returns {@code null}.
+ *
+ *
+ * @param name The name of the field to retrieve. Must not be {@code null}.
+ * @param The expected type of the field value.
+ * @return The field value cast to type {@code T}, or {@code null} if absent.
+ * @throws ClassCastException If the stored value cannot be cast to {@code T}.
+ */
+ @SuppressWarnings("unchecked")
+ public T getField(String name) {
+ return (T) fields.get(name);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/vm/module/VirtualTable.java b/src/main/java/org/jcnc/snow/vm/module/VirtualTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7e0fe6abfec6678b18a09052c15c599e621ed54
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/module/VirtualTable.java
@@ -0,0 +1,70 @@
+package org.jcnc.snow.vm.module;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The {@code VirtualTable} class represents a runtime virtual function table (vtable)
+ * used by the virtual machine to support dynamic method dispatch.
+ *
+ * A vtable is a mapping from method signatures to their corresponding
+ * entry addresses in the VM code segment. Each class or instance can have
+ * its own vtable that determines which concrete method implementations
+ * are invoked at runtime.
+ *
+ *
+ * Key details:
+ *
+ * Key: A method signature, such as {@code "Person::getName"}.
+ * Value: The entry point address of the method implementation
+ * within the VM code segment.
+ *
+ */
+public final class VirtualTable {
+
+ /** Mapping from method signature to its entry address in the VM code segment. */
+ private final Map table = new HashMap<>();
+
+ /**
+ * Registers a new method implementation in the virtual table, or overrides
+ * an existing entry if the method signature is already present.
+ *
+ * @param methodSig The method signature (e.g., {@code "Person::getName"}).
+ * @param addr The entry address of the method implementation in the VM code segment.
+ */
+ public void register(String methodSig, int addr) {
+ table.put(methodSig, addr);
+ }
+
+ /**
+ * Looks up the entry address for the given method signature.
+ *
+ * If the method is not found in the virtual table, an {@link IllegalStateException}
+ * is thrown to aid debugging.
+ *
+ *
+ * @param methodSig The method signature to look up.
+ * @return The entry address of the corresponding method implementation.
+ * @throws IllegalStateException If no mapping exists for the given method signature.
+ */
+ public int lookup(String methodSig) {
+ Integer addr = table.get(methodSig);
+ if (addr == null)
+ throw new IllegalStateException("VTable missing entry: " + methodSig);
+ return addr;
+ }
+
+ /**
+ * Returns a read-only snapshot of the current virtual table.
+ *
+ * This is primarily intended for debugging or inspection purposes,
+ * allowing external code to see the current state of the vtable
+ * without modifying it.
+ *
+ *
+ * @return An unmodifiable copy of the method signature to entry address mappings.
+ */
+ public Map snapshot() {
+ return Map.copyOf(table);
+ }
+}