Compare commits

..

No commits in common. "feature/add-struct" and "main" have entirely different histories.

78 changed files with 1354 additions and 3891 deletions

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo1 -o target/Demo1" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo1 -o target/Demo1 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo10 -o target/Demo10" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo10 -o target/Demo10 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo11 -o target/Demo11" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo11 -o target/Demo11 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo12 -o target/Demo12" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo12 -o target/Demo12 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo13" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo13 -o target/Demo13" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo13 -o target/Demo13 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo15" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo15 -o target/Demo15" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo15 -o target/Demo15 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo16" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo16 -o target/Demo16 " />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo16 -o target/Demo16 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo17" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo17 -o target/Demo17" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo17 -o target/Demo17 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo18" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo18 -o target/Demo18" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo18 -o target/Demo18 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo19" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo19 -o target/Demo19" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo19 -o target/Demo19 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo2 -o target/Demo2" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo2 -o target/Demo2 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo20" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo20 -o target/Demo20" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo20 -o target/Demo20 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo21" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo21 -o target/Demo21" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo21 -o target/Demo21 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Demo23" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo23 -o target/Demo23" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo23 -o target/Demo23 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -1,10 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo26" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo26 -o target/Demo26" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,10 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo27" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo27 -o target/Demo27" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,10 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo28" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo28 -o target/Demo28" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,10 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo29" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo29 -o target/Demo29" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo3 -o target/Demo3" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo3 -o target/Demo3 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -1,10 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo30" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo30 -o target/Demo30" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,10 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo31" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo31 -o target/Demo31 " />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,10 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Demo32" type="Application" factoryName="Application" folderName="Demo">
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo32 -o target/Demo32" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo4 -o target/Demo4" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo4 -o target/Demo4 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo5 -o target/Demo5" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo5 -o target/Demo5 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo6 -o target/Demo6" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo6 -o target/Demo6 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo7 -o target/Demo7" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo7 -o target/Demo7 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo8 -o target/Demo8" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo8 -o target/Demo8 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -3,7 +3,7 @@
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
<module name="Snow" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo9 -o target/Demo9" />
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo9 -o target/Demo9 --debug" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@ -26,33 +26,4 @@
<toRun name="Demo9" type="Application" />
<method v="2" />
</configuration>
<configuration default="false" name="测试" type="CompoundRunConfigurationType">
<toRun name="Demo1" type="Application" />
<toRun name="Demo10" type="Application" />
<toRun name="Demo11" type="Application" />
<toRun name="Demo12" type="Application" />
<toRun name="Demo13" type="Application" />
<toRun name="Demo14" type="Application" />
<toRun name="Demo15" type="Application" />
<toRun name="Demo16" type="Application" />
<toRun name="Demo17" type="Application" />
<toRun name="Demo18" type="Application" />
<toRun name="Demo19" type="Application" />
<toRun name="Demo2" type="Application" />
<toRun name="Demo20" type="Application" />
<toRun name="Demo21" type="Application" />
<toRun name="Demo22" type="Application" />
<toRun name="Demo23" type="Application" />
<toRun name="Demo24" type="Application" />
<toRun name="Demo25" type="Application" />
<toRun name="Demo26" type="Application" />
<toRun name="Demo28" type="Application" />
<toRun name="Demo3" type="Application" />
<toRun name="Demo4" type="Application" />
<toRun name="Demo6" type="Application" />
<toRun name="Demo7" type="Application" />
<toRun name="Demo8" type="Application" />
<toRun name="Demo9" type="Application" />
<method v="2" />
</configuration>
</component>

View File

@ -1,74 +0,0 @@
// 定义模块
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

View File

@ -1,135 +0,0 @@
// 演示 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

View File

@ -1,54 +0,0 @@
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

View File

@ -1,18 +0,0 @@
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

View File

@ -1,65 +0,0 @@
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

View File

@ -1,18 +0,0 @@
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

View File

@ -1,66 +0,0 @@
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

View File

@ -1,18 +0,0 @@
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

View File

@ -1,56 +0,0 @@
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

View File

@ -1,18 +0,0 @@
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

View File

@ -1,55 +0,0 @@
module: main
import: os
struct: Student
fields:
declare name: int
declare studentId: int
init:
params:
name: int
studentId: int
body:
this.name = name
this.studentId = studentId
end body
end init
init:
params:
body:
this.name = 0
this.studentId = 0
end body
end init
init:
params:
name: int
body:
this.name = name
this.studentId = -1
end body
end init
function: getStudentId
returns: int
body:
return this.studentId
end body
end function
end struct
function: main
returns: void
body:
declare a: Student = new Student(123, 1001)
declare b: Student = new Student()
declare c: Student = new Student(88)
println(a.getStudentId())
println(b.getStudentId())
println(b.getStudentId())
end body
end function
end module

View File

@ -1,18 +0,0 @@
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

View File

@ -1,66 +1,50 @@
package org.jcnc.snow.compiler.backend.builder;
import org.jcnc.snow.vm.engine.VMOpCode;
import java.util.*;
/**
* {@code VMProgramBuilder} 负责后端阶段的 VM 指令序列组装及符号修补
* VMProgramBuilder 用于构建虚拟机(VM)的最终指令列表
* <p>
* 主要职责
* <ul>
* <li>管理函数与标签到指令地址的映射</li>
* <li>支持 CALL 和分支指令的延迟回填符号修补</li>
* <li>支持槽位类型标注用于类型检查和后端优化可选</li>
* </ul>
*
* <p><b>符号修补机制</b></p>
* <ul>
* <li>支持根据函数简名进行匹配例如 Student.getName 可匹配到 Person.getName实现方法复用和继承支持</li>
* <li>自动处理 super 调用的符号绑定例如 Student.super 可自动绑定到 Person.__init__</li>
* <li>所有未解决的 CALL 或分支符号在 build() 阶段将抛出异常便于调试和定位错误</li>
* <li>维护代码指令序列和符号地址表</li>
* <li>支持跨函数标签跳转的延后修补(Call/Branch Fixup)</li>
* <li>支持虚拟机本地槽位类型的管理( I/F...)</li>
* </ul>
*/
public final class VMProgramBuilder {
/**
* 未解析地址的占位符便于后期批量修补
* 未知目标的 CALL 指令修补记录(待目标地址确定后修正)
* @param index CALL 指令在 code 列表中的位置
* @param target 目标函数的全名
* @param nArgs 参数个数
*/
private static final String PLACEHOLDER = "-1";
/**
* VM 指令列表
*/
private final List<String> code = new ArrayList<>();
/**
* 槽位(寄存器)类型映射表( I/F...用于类型检查或代码生成优化)
*/
private final Map<Integer, Character> slotType = new HashMap<>();
/**
* 符号(函数名/标签)到指令序号的映射表
*/
private final Map<String, Integer> addr = new HashMap<>();
/**
* 所有待修补的 CALL 指令集合
*/
private final List<CallFix> callFixes = new ArrayList<>();
/**
* 所有待修补的分支指令集合
*/
private final List<BranchFix> branchFixes = new ArrayList<>();
/**
* 当前代码指针(已生成指令的数量/下一个指令的位置)
*/
private int pc = 0;
private record CallFix(int index, String target, int nArgs) {}
/**
* 获取符号名最后一段即最后一个 '.' 之后的名字若无 '.' 则为原名
*
* @param sym 符号全名
* @return 简名
* 未知目标的分支指令修补记录(待目标标签确定后修正)
* @param index 分支指令在 code 列表中的位置
* @param label 跳转目标标签名
*/
private static String lastSegment(String sym) {
int p = sym.lastIndexOf('.');
return (p >= 0 && p < sym.length() - 1) ? sym.substring(p + 1) : sym;
}
private record BranchFix(int index, String label) {}
/** 未解析地址的占位符,便于后期批量修补 */
private static final String PLACEHOLDER = "-1";
/** VM 指令列表 */
private final List<String> code = new ArrayList<>();
/** 槽位(寄存器)类型映射表(如 I/F...,用于类型检查或代码生成优化) */
private final Map<Integer, Character> slotType = new HashMap<>();
/** 符号(函数名/标签)到指令序号的映射表 */
private final Map<String, Integer> addr = new HashMap<>();
/** 所有待修补的 CALL 指令集合 */
private final List<CallFix> callFixes = new ArrayList<>();
/** 所有待修补的分支指令集合 */
private final List<BranchFix> branchFixes = new ArrayList<>();
/** 当前代码指针(已生成指令的数量/下一个指令的位置) */
private int pc = 0;
/**
* 设置槽位(局部变量/虚拟寄存器)的类型前缀
@ -72,8 +56,6 @@ public final class VMProgramBuilder {
slotType.put(slot, prefix);
}
// ============================== 槽位类型操作 ==============================
/**
* 获取槽位的类型前缀默认为 'I'(整数类型)
*
@ -85,9 +67,12 @@ public final class VMProgramBuilder {
}
/**
* 声明一个函数/标签的起始地址并尝试修补所有引用到此符号的 CALL/BRANCH
* 标记一个函数或标签的起始位置
* <p>
* 1. 记录符号到当前 pc 的映射
* 2. 立即尝试修补之前所有针对该符号的延后调用和分支
*
* @param name 函数或标签全名 "Person.getName""loop.start"
* @param name 函数名或标签名(全限定名)
*/
public void beginFunction(String name) {
addr.put(name, pc);
@ -95,21 +80,17 @@ public final class VMProgramBuilder {
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);
@ -120,10 +101,10 @@ public final class VMProgramBuilder {
}
/**
* 添加一条 CALL 指令目标尚未声明时使用占位符并登记延迟修补
* 添加一条 CALL 指令若目标未定义则延后修补
*
* @param target 目标函数全名IR 侧生成
* @param nArgs 参个数
* @param target 目标函数全名
* @param nArgs 个数
*/
public void emitCall(String target, int nArgs) {
Integer a = resolve(target);
@ -144,9 +125,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));
}
}
@ -162,8 +143,8 @@ public final class VMProgramBuilder {
public List<String> build() {
if (!callFixes.isEmpty() || !branchFixes.isEmpty()) {
throw new IllegalStateException(
"Unresolved symbols — CALL: " + callFixes + ", BRANCH: " + branchFixes
);
"Unresolved symbols — CALL: " + callFixes +
", BRANCH: " + branchFixes);
}
return List.copyOf(code);
}
@ -179,36 +160,17 @@ public final class VMProgramBuilder {
}
/**
* 修补所有延迟 CALL
* 匹配规则
* 1. 全限定名完全匹配
* 2. super 调用支持多构造target .super 结尾name .__init__N 结尾且前缀匹配
* 3. 简名匹配 __init__N
* 修补所有等待目标函数 name CALL 指令
* <p>
* 只支持全名精确修补不做模糊查找或短名回退
*
* @param name 目标函数全名
*/
private void patchCallFixes(String name) {
String nameSimple = lastSegment(name);
for (Iterator<CallFix> it = callFixes.iterator(); it.hasNext();) {
CallFix f = it.next();
// 全限定名完全匹配
boolean qualifiedMatch = f.target.equals(name);
// super 调用绑定target .supername .__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__"));
superMatch = tStruct.equals(nStruct);
}
// 简名匹配 getName, __init__N
String targetSimple = lastSegment(f.target);
boolean simpleMatch = targetSimple.equals(nameSimple);
if (qualifiedMatch || superMatch || simpleMatch) {
int targetAddr = addr.get(name);
code.set(f.index, VMOpCode.CALL + " " + targetAddr + " " + f.nArgs);
if (f.target.equals(name)) {
code.set(f.index, VMOpCode.CALL + " " + addr.get(name) + " " + f.nArgs);
it.remove();
}
}
@ -229,23 +191,4 @@ public final class VMProgramBuilder {
}
}
}
/**
* 未知目标的 CALL 指令修补记录(待目标地址确定后修正)
*
* @param index CALL 指令在 code 列表中的位置
* @param target 目标函数的全名
* @param nArgs 参数个数
*/
private record CallFix(int index, String target, int nArgs) {
}
/**
* 未知目标的分支指令修补记录(待目标标签确定后修正)
*
* @param index 分支指令在 code 列表中的位置
* @param label 跳转目标标签名
*/
private record BranchFix(int index, String label) {
}
}

View File

@ -16,62 +16,40 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* CallGenerator 负责将 IR 层的 CallInstruction 转换为 VM 层的函数调用指令
* {@code CallGenerator} 负责将 IR 层的 {@link CallInstruction} 生成对应的 VM 函数调用指令
* <p>
* 功能包括
* 支持 syscall普通函数调用一维/多维数组下标访问以及字符串常量池绑定等功能
* </p>
*
* <ul>
* <li>处理系统调用syscall支持字符串常量池</li>
* <li>数组元素访问及赋值__index / __setindex 系列</li>
* <li>普通函数调用根据返回类型选择合适的 STORE 指令</li>
* <li>syscall: 支持字符串常量与寄存器到子命令的绑定与解析</li>
* <li>数组下标访问: 支持所有主流基础类型 __index_b/s/i/l/f/d/r</li>
* <li>普通函数: 支持自动推断返回值类型与槽位类型</li>
* </ul>
*/
public class CallGenerator implements InstructionGenerator<CallInstruction> {
/**
* 字符串常量池用于 syscall 子命令的字符串存储
* 键为虚拟寄存器 ID值为字符串常量
* 字符串常量池用于绑定虚拟寄存器 id 到字符串值 syscall 子命令使用
* <br>
* 使用 ConcurrentHashMap 保证并发安全所有 registerStringConst 的注册和读取都是线程安全的
*/
private static final Map<Integer, String> STRING_CONST_POOL = new ConcurrentHashMap<>();
/**
* 当前正在生成的函数名用于错误上下文
*/
private String fn;
/**
* 注册字符串常量到常量池
* 注册一个字符串常量绑定到虚拟寄存器 id
*
* @param regId 虚拟寄存器 ID
* @param value 字符串常量
* @param regId 虚拟寄存器 id
* @param value 字符串常量
*/
public static void registerStringConst(int regId, String value) {
STRING_CONST_POOL.put(regId, value);
}
/**
* 将语言层类型名映射为 VM 指令的类型前缀字符
* 返回本生成器支持的 IR 指令类型
*
* @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
* @return {@link CallInstruction} 类型的 Class 对象
*/
@Override
public Class<CallInstruction> supportedClass() {
@ -79,12 +57,12 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
}
/**
* 核心生成方法根据函数名分发到 syscall数组操作或普通调用的处理逻辑
* 生成指定的调用指令包括 syscall数组下标普通函数
*
* @param ins 待转换的 IR 调用指令
* @param out VM 程序构建器用于输出指令
* @param slotMap IR 虚拟寄存器到 VM 本地槽位的映射
* @param currentFn 当前所在的函数名用于上下文传递
* @param ins IR 层的调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位的映射
* @param currentFn 当前函数名
*/
@Override
public void generate(CallInstruction ins,
@ -93,13 +71,13 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
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');
@ -129,6 +107,7 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
generateIndexInstruction(ins, out, slotMap, 'R');
return;
}
case "__setindex_b" -> {
generateSetIndexInstruction(ins, out, slotMap, 'B');
return;
@ -159,17 +138,24 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
}
}
// 默认普通函数调用
// 普通函数调用
generateNormalCall(ins, out, slotMap, fn);
}
// ========== 私有辅助方法 ==========
/**
* 生成数组元素赋值指令
* 生成数组元素赋值指令arr[idx] = value无返回值
* <p>
* 调用栈压栈顺序为arr (引用类型, R)idx (整型, I)value (元素类型, T)
* 执行 SYSCALL ARR_SET 指令以完成数组赋值操作
* </p>
*
* @param ins 调用 __setindex_* IR 指令
* @param out VM 指令构建器
* @param slotMap 寄存器到槽位映射
* @param valType 元素值的类型前缀 'I','R'
* @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则抛出异常
*/
private void generateSetIndexInstruction(CallInstruction ins,
VMProgramBuilder out,
@ -177,189 +163,264 @@ public class CallGenerator implements InstructionGenerator<CallInstruction> {
char valType) {
List<IRValue> 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);
}
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");
// 第一个参数为数组对象压入引用类型寄存器'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");
}
/**
* 生成数组元素读取指令
* 解析 syscall 子命令第一个参数支持字符串常量与已绑定字符串的虚拟寄存器
*
* @param ins 调用 __index_* IR 指令
* @param out VM 指令构建器
* @param slotMap 寄存器到槽位映射
* @param retType 返回值类型前缀
*/
private void generateIndexInstruction(CallInstruction ins,
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
char retType) {
String fn = ins.getFunctionName();
List<IRValue> args = ins.getArguments();
if (args.size() != 2) {
throw new IllegalStateException("[CallGenerator] " + fn + " 需要两个参数 (arr, idx),实际: " + args);
}
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] " + fn + " 必须有返回值寄存器");
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException("[CallGenerator] " + fn + " 未找到目标槽位");
}
// 根据类型选择 STORE 指令
switch (retType) {
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指令
* 首个参数为子命令支持常量或寄存器引用其余参数均以引用形式加载
* 若存在返回寄存器则将结果存为整数类型
*
* @param ins syscall 对应的 IR 调用指令
* @param out VM 指令构建器
* @param slotMap 寄存器到槽位的映射
* @param fn 函数名"syscall" 或以 ".syscall" 结尾
*/
private void generateSyscall(CallInstruction ins,
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
String fn) {
List<IRValue> args = ins.getArguments();
if (args.isEmpty()) {
throw new IllegalStateException("[CallGenerator] syscall 至少需要一个子命令");
}
String subcmd = resolveSyscallSubcmd(args.get(0), fn);
for (int i = 1; i < args.size(); i++) {
loadArgument(out, slotMap, args.get(i), 'R', fn);
}
out.emit(VMOpCode.SYSCALL + " " + subcmd);
IRVirtualRegister dest = ins.getDest();
if (dest != null) {
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 参数类型或常量池缺失时抛出
* @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) {
switch (arg) {
case IRConstant(String s) -> {
return s.toUpperCase(Locale.ROOT);
}
throw new IllegalStateException("[CallGenerator] syscall 子命令必须是字符串 (函数: " + fn + ")");
}
if (arg instanceof IRVirtualRegister(int id)) {
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 字符串常量");
if (s == null) {
throw new IllegalStateException(
"[CallGenerator] 未找到 syscall 字符串常量绑定 (function: %s, regId: %d)"
.formatted(fn, id)
);
}
return s.toUpperCase(Locale.ROOT);
}
throw new IllegalStateException("[CallGenerator] syscall 子命令参数错误: " + arg);
case null, default -> throw new IllegalStateException(
"[CallGenerator] syscall 第一个参数必须是字符串常量或已绑定字符串的寄存器 (function: %s, arg: %s)"
.formatted(fn, arg)
);
}
}
/**
* 生成普通函数调用指令
* 参数均以引用形式压栈调用完成后若返回类型非 void
* 则选择合适的 STORE 指令保存结果
* 加载一个参数到栈支持指定默认类型如果未设置类型则采用默认类型
*
* @param ins 调用指令
* @param out 指令构建器
* @param slotMap 寄存器到槽位映射
* @param fn 被调用函数名称
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位的映射
* @param arg 参数值应为虚拟寄存器
* @param defaultType 默认类型
* @param fn 当前函数名用于错误提示
* @throws IllegalStateException 如果参数不是虚拟寄存器或槽位未找到
*/
private void generateNormalCall(CallInstruction ins,
VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap,
String fn) {
String retTypeName = GlobalFunctionTable.getReturnType(fn);
char retType = normalizeTypePrefix(retTypeName);
for (IRValue arg : ins.getArguments()) {
loadArgument(out, slotMap, arg, 'R', fn);
}
out.emitCall(fn, ins.getArguments().size());
if ("void".equals(retTypeName)) {
return;
}
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
throw new IllegalStateException("[CallGenerator] 普通调用缺少返回寄存器");
}
Integer slot = slotMap.get(dest);
if (slot == null) {
throw new IllegalStateException("[CallGenerator] 未找到返回槽位");
}
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<IRVirtualRegister, Integer> slotMap,
IRValue arg,
char defaultType,
String fn) {
this.fn = fn;
private void loadArgument(VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, IRValue arg, char defaultType, String fn) {
if (!(arg instanceof IRVirtualRegister vr)) {
throw new IllegalStateException("[CallGenerator] 参数必须是 IRVirtualRegister");
throw new IllegalStateException(
"[CallGenerator] 参数必须为虚拟寄存器 (function: %s, arg: %s)".formatted(fn, arg));
}
Integer slot = slotMap.get(vr);
if (slot == null) {
throw new IllegalStateException("[CallGenerator] 未找到参数槽位");
throw new IllegalStateException(
"[CallGenerator] 未找到虚拟寄存器的槽位映射 (function: %s, reg: %s)".formatted(fn, vr));
}
char t = out.getSlotType(slot);
if (t == '\0' || (t == 'I' && defaultType != 'I')) {
t = defaultType;
}
if (t == '\0') t = defaultType;
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slot);
}
/**
* 返回最近一次处理的函数名可用于调试
* 生成一维/多维数组的下标访问指令支持类型分派
* <p>
* 本方法用于将 IR 层的数组下标访问表达式 arr[idx]生成对应的 VM 指令序列
* 支持 byte/short/int/long/float/double/reference 等所有基础类型的数组元素访问
* </p>
*
* @return 函数名字符串
* <ul>
* <li>1. 依次加载数组参数arr和下标参数idx到操作数栈</li>
* <li>2. 发出 ARR_GET 系统调用指令SYSCALL ARR_GET通过 VM 访问对应元素</li>
* <li>3. 根据元素声明类型将结果写入目标槽位支持类型分派B/S/I/L/F/D/R</li>
* <li>4. 若参数或返回寄存器缺失则抛出异常提示</li>
* </ul>
*
* @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 参数个数不符缺少目标寄存器未找到槽位等情况
*/
public String getFn() {
return fn;
private void generateIndexInstruction(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, char retType) {
String fn = ins.getFunctionName();
List<IRValue> args = ins.getArguments();
if (args.size() != 2) {
throw new IllegalStateException(
"[CallGenerator] %s 需要两个参数(arr, idx),实际: %s".formatted(fn, 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");
// 保存返回值到目标寄存器
IRVirtualRegister dest = ins.getDest();
if (dest == null) {
throw new IllegalStateException(
"[CallGenerator] %s 需要有目标寄存器用于保存返回值".formatted(fn));
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] %s 未找到目标寄存器的槽位映射 (dest: %s)".formatted(fn, dest));
}
// 按元素类型分派写入 VM 槽位
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');
}
}
}
/**
* 生成 syscall 指令分支逻辑
* <ol>
* <li>解析 syscall 子命令</li>
* <li>压栈剩余参数</li>
* <li>发出 SYSCALL 指令</li>
* <li>若有返回值则保存至目标槽位</li>
* </ol>
*
* @param ins 调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位映射
* @param fn 当前函数名
*/
private void generateSyscall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
List<IRValue> args = ins.getArguments();
if (args.isEmpty()) {
throw new IllegalStateException(
"[CallGenerator] syscall 需要子命令参数 (function: %s)".formatted(fn));
}
// 0. 解析 syscall 子命令第一个参数
String subcmd = resolveSyscallSubcmd(args.getFirst(), fn);
// 1. 压栈其余 syscall 参数 index 1 开始
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));
}
out.emit(OpHelper.opcode("I_STORE") + " " + destSlot);
out.setSlotType(destSlot, 'I');
}
}
/**
* 生成普通函数调用指令
* <ol>
* <li>推断返回值类型</li>
* <li>压栈所有参数</li>
* <li>生成 CALL 指令</li>
* <li>保存返回值若非 void</li>
* </ol>
*
* @param ins 调用指令
* @param out VM 程序构建器
* @param slotMap 寄存器到槽位映射
* @param fn 当前函数名
*/
private void generateNormalCall(CallInstruction ins, VMProgramBuilder out, Map<IRVirtualRegister, Integer> slotMap, String fn) {
// 1. 推断返回值类型首字母大写缺省为 'I'
String retTypeName = GlobalFunctionTable.getReturnType(fn);
char retType = (retTypeName != null && !retTypeName.isEmpty()) ? Character.toUpperCase(retTypeName.charAt(0)) : 'I';
// 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));
}
Integer destSlot = slotMap.get(dest);
if (destSlot == null) {
throw new IllegalStateException(
"[CallGenerator] 普通函数调用未找到目标寄存器的槽位映射 (function: %s, dest: %s)".formatted(fn, dest));
}
out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
out.setSlotType(destSlot, retType);
}
}

View File

@ -43,7 +43,12 @@ public record ExpressionBuilder(IRContext ctx) {
// 布尔字面量例如 true / false
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
// 标识符变量名 ab
case IdentifierNode id -> buildIdentifier(id);
case IdentifierNode id -> {
// 查找当前作用域中的变量寄存器
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
yield reg;
}
// 模块常量 / 全局变量 ModuleA.a
case MemberExpressionNode mem -> buildMember(mem);
// 二元表达式 a+b, x==y
@ -54,7 +59,6 @@ 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());
@ -62,182 +66,76 @@ public record ExpressionBuilder(IRContext ctx) {
}
/**
* 构造标识符节点对应的 IR 虚拟寄存器
* <p>
* 支持普通变量查找以及结构体方法/构造器中的字段回退机制即自动将裸标识符回退为 this.<id>
* 成员访问表达式构建
*
* @param id 标识符节点
* @return 查找到的 IR 虚拟寄存器
* @throws IllegalStateException 若标识符未定义且无法回退为字段
*/
private IRVirtualRegister buildIdentifier(IdentifierNode id) {
// ====================== 普通变量查找 ======================
// 1. 在当前作用域查找变量可能是局部变量形参临时变量等
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
if (reg != null) return reg;
// ====================== 字段回退机制 ======================
// 2. 若未找到则判断是否处于结构体方法或构造器中
// 尝试将裸标识符自动视为 this.<id>即访问当前结构体实例的成员字段
IRVirtualRegister thisReg = ctx.getScope().lookup("this");
String thisType = ctx.getScope().lookupType("this");
if (thisReg != null && thisType != null) {
// 生成成员表达式节点 this.<id>
MemberExpressionNode asField = new MemberExpressionNode(
new IdentifierNode("this", id.context()), // 构造 this 节点
id.name(), // 字段名
id.context()
);
// 递归构造成员访问相当于构造 this.<id> IR
return buildMember(asField);
}
// ====================== 标识符未定义异常 ======================
// 3. 无法查找到变量且不能字段回退抛出异常
throw new IllegalStateException("未定义标识符: " + id.name());
}
/**
* 构建成员访问表达式的 IR 虚拟寄存器
* <p>
* 支持两类成员访问
* <ul>
* <li>模块常量访问 ModuleName.CONST</li>
* <li>结构体/对象字段访问 obj.field this.field</li>
* </ul>
*
* @param mem 成员访问表达式节点 a.b ModuleName.CONST
* @return 存储成员值的 IR 虚拟寄存器
* @throws IllegalStateException 若找不到成员或无法解析类型
* @param mem 成员表达式节点
* @return 存储结果的虚拟寄存器
*/
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 指令
if (!(mem.object() instanceof IdentifierNode id)) {
throw new IllegalStateException("不支持的成员访问对象类型: "
+ mem.object().getClass().getSimpleName());
}
String qualified = id.name() + "." + mem.member();
/* 1) 尝试直接获取已有寄存器绑定 */
IRVirtualRegister reg = ctx.getScope().lookup(qualified);
if (reg != null) {
return reg;
}
/* 2) 折叠为编译期常量:先查作用域,再查全局常量表 */
Object v = ctx.getScope().getConstValue(qualified);
if (v == null) {
v = GlobalConstTable.get(qualified);
}
if (v != null) {
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(c)));
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(v)));
return r;
}
throw new IllegalStateException("未定义的常量: " + qualified);
}
// ===== 2. 结构体/对象字段访问 =====
// 1 递归构建成员对象 a.b先获得 a 的寄存器
IRVirtualRegister objReg = build(mem.object());
// 2 尝试解析成员访问接收者object的类型
String ownerType = null;
if (mem.object() instanceof IdentifierNode oid) {
// 如果对象是标识符直接查询其类型例如 a.xa 的类型
ownerType = ctx.getScope().lookupType(oid.name());
}
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("无法解析成员访问接收者的类型");
}
// 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<IRValue> 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}
* <p>
* 根据不同表达式类型采取高效或递归方式生成中间代码涵盖常量变量运算数组调用等
* 按表达式类型分派处理包括
* <ul>
* <li>字面量数字字符串布尔数组生成 loadConst 指令直接写入目标寄存器</li>
* <li>变量标识符查表获取源寄存器 move 到目标寄存器</li>
* <li>二元表达式下标调用表达式递归生成子表达式结果并写入目标寄存器</li>
* <li>其它类型统一先 build 到临时寄存器 move 到目标寄存器</li>
* </ul>
* </p>
*
* @param node 表达式节点可为字面量变量数组运算等
* @param dest 目标虚拟寄存器写入结果
* @throws IllegalStateException 若变量标识符未定义
* @param node 要求值的表达式节点
* @param dest 结果目标虚拟寄存器
* @throws IllegalStateException 若标识符未定义如变量未声明时引用
*/
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) {
// ===================== 数字字面量 =====================
// 423.14 直接生成 loadConst 指令常量写入目标寄存器
// 数字字面量生成 loadConst 指令将数值常量写入目标寄存器
case NumberLiteralNode n -> InstructionFactory.loadConstInto(
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
// ===================== 字符串字面量 =====================
// "hello"直接生成 loadConst 指令
// 字符串字面量生成 loadConst 指令将字符串常量写入目标寄存器
case StringLiteralNode s -> InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(s.value()));
// ===================== 布尔字面量 =====================
// true/false转换为 int 常量 1/0生成 loadConst
// 布尔字面量转换为 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));
// ===================== new 表达式构造数组/对象 =====================
// 生成空数组然后按参数依次初始化每一项使用 __setindex_r 填充目标寄存器
case NewExpressionNode newExpr -> {
// 1. 把空列表写入目标寄存器
InstructionFactory.loadConstInto(ctx, dest, new IRConstant(java.util.List.of()));
// 2. 依次写入构造实参同时缓存参数寄存器
List<IRVirtualRegister> 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. 若确认为结构体,显式调用 <Struct>.__init__N 完成字段初始化 */
if (IRBuilderScope.getStructLayout(newExpr.typeName()) != null) {
String ctor = newExpr.typeName() + ".__init__" + argRegs.size();
List<IRValue> ctorArgs = new ArrayList<>();
ctorArgs.add(dest); // this
ctorArgs.addAll(argRegs); // 实参
ctx.addInstruction(new CallInstruction(null, ctor, ctorArgs));
}
}
// ===================== 变量标识符 =====================
// x查找符号表move 到目标寄存器未定义时报错
// 变量标识符查表获得源寄存器move 到目标寄存器
case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null)
@ -245,26 +143,22 @@ public record ExpressionBuilder(IRContext ctx) {
InstructionFactory.move(ctx, src, dest);
}
// ===================== 二元表达式 a + b =====================
// 递归生成左右操作数并将运算结果写入目标寄存器
// 二元表达式递归生成左右子表达式并将结果写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
// ===================== 下标访问 arr[1] =====================
// 先递归构造表达式将索引结果 move 到目标寄存器
// 下标表达式递归生成索引结果move 到目标寄存器
case IndexExpressionNode idx -> {
IRVirtualRegister tmp = buildIndex(idx);
InstructionFactory.move(ctx, tmp, dest);
}
// ===================== 函数/方法调用 foo(a, b) =====================
// 递归生成调用结果 move 到目标寄存器
// 调用表达式递归生成调用结果move 到目标寄存器
case CallExpressionNode call -> {
IRVirtualRegister tmp = buildCall(call);
InstructionFactory.move(ctx, tmp, dest);
}
// ===================== 其它所有情况兜底处理 =====================
// 通用流程先生成结果到临时寄存器 move 到目标寄存器
// 其它类型统一先 build 到临时寄存器 move 到目标寄存器
default -> {
IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest);
@ -421,13 +315,10 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 编译期常量值支持 intdoubleStringList否则返回 null
*/
private Object tryFoldConst(ExpressionNode expr) {
switch (expr) {
case null -> {
return null;
}
if (expr == null) return null;
// 数字字面量尝试解析为 int double
case NumberLiteralNode n -> {
if (expr instanceof NumberLiteralNode n) {
String s = n.value();
try {
if (s.contains(".") || s.contains("e") || s.contains("E"))
@ -438,19 +329,14 @@ public record ExpressionBuilder(IRContext ctx) {
}
}
// 字符串字面量直接返回字符串
case StringLiteralNode s -> {
return s.value();
}
if (expr instanceof StringLiteralNode s) return s.value();
// 布尔字面量true 返回 1false 返回 0
case BoolLiteralNode b -> {
return b.getValue() ? 1 : 0;
}
if (expr instanceof BoolLiteralNode b) return b.getValue() ? 1 : 0;
// 数组字面量递归折叠所有元素
case ArrayLiteralNode arr -> {
if (expr instanceof ArrayLiteralNode arr) {
List<Object> list = new ArrayList<>();
for (ExpressionNode e : arr.elements()) {
Object v = tryFoldConst(e);
@ -460,9 +346,8 @@ public record ExpressionBuilder(IRContext ctx) {
return List.copyOf(list);
}
// 标识符尝试查找作用域中的常量值
case IdentifierNode id -> {
if (expr instanceof IdentifierNode id) {
Object v = null;
try {
v = ctx.getScope().getConstValue(id.name());
@ -471,9 +356,6 @@ public record ExpressionBuilder(IRContext ctx) {
}
return v;
}
default -> {
}
}
// 其它类型不支持折叠返回 null
return null;
@ -515,139 +397,47 @@ public record ExpressionBuilder(IRContext ctx) {
/**
* new 对象/数组创建表达式 IR 虚拟寄存器支持列表型构造
* 函数或方法调用表达式
* <p>
* 语义说明本方法用于生成new 表达式的中间代码流程具体包括
* 支持普通函数调用foo(a, b)与成员方法调用obj.method(a, b)
* <ul>
* <li>分配一个新的寄存器用于保存新对象/数组引用</li>
* <li>将一个空列表常量写入该寄存器后端 runtime 识别为可变列表/对象</li>
* <li>遍历所有构造参数依次写入目标列表的 [0..n-1] 位置</li>
* <li>最终返回该寄存器</li>
* <li>首先递归生成所有参数的虚拟寄存器列表</li>
* <li>根据 callee 类型区分成员访问或直接标识符调用并规范化方法名如加前缀</li>
* <li>为返回值分配新寄存器生成 Call 指令</li>
* </ul>
* </p>
*
* @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<IRVirtualRegister> 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. 若为结构体实例,调用构造器 <Struct>.__init__N */
if (IRBuilderScope.getStructLayout(node.typeName()) != null) {
String ctorName = node.typeName() + ".__init__" + argRegs.size();
List<IRValue> ctorArgs = new ArrayList<>();
ctorArgs.add(dest); // 隐式 this
ctorArgs.addAll(argRegs); // 构造实参
ctx.addInstruction(new CallInstruction(null, ctorName, ctorArgs));
}
return dest;
}
/**
* 构建函数或方法调用表达式的 IR 指令并返回结果寄存器
* <p>
* 支持三类调用
* <ul>
* <li>普通函数调用foo(a, b)</li>
* <li>成员方法调用obj.method(a, b) ModuleName.func(a, b)</li>
* <li>链式方法调用obj.getAddr().getCity()</li>
* </ul>
* 核心流程
* <ol>
* <li>递归生成所有参数的虚拟寄存器列表</li>
* <li>根据 callee 类型区分结构体方法/模块静态函数/普通函数</li>
* <li>规范化被调用方法名并整理最终参数表</li>
* <li>分配用于结果的新寄存器生成 Call 指令</li>
* </ol>
*
* @param call 函数或方法调用表达式节点
* @return 存放调用结果的目标虚拟寄存器
* @throws IllegalStateException 被调用表达式类型不支持或参数异常
* @param call 函数/方法调用表达式节点
* @return 存放调用结果的虚拟寄存器
*/
private IRVirtualRegister buildCall(CallExpressionNode call) {
// 1. 先递归生成显式参数的寄存器列表
List<IRVirtualRegister> explicitRegs = new ArrayList<>();
for (ExpressionNode a : call.arguments()) explicitRegs.add(build(a));
// 1. 递归生成所有参数的寄存器
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
String callee; // 函数/方法的规范化名字
List<IRValue> finalArgs = new ArrayList<>(); // 最终传递给 CALL 的参数表
// 2. callee 类型分支
switch (call.callee()) {
case MemberExpressionNode m when m.object() instanceof IdentifierNode idObj -> {
// ========= 情况 1: obj.method(...) ModuleName.func(...) =========
String recvName = idObj.name();
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("未定义标识符: " + recvName);
finalArgs.add(thisReg); // 隐式 this
finalArgs.addAll(explicitRegs); // 其它参数
}
}
case MemberExpressionNode m -> {
// ========= 情况 2: 通用成员调用 (支持链式调用) =========
// 例如 p.getAddr().getCity()
IRVirtualRegister objReg = build(m.object()); // 递归计算 object 表达式
callee = m.member(); // 直接用成员名
finalArgs.add(objReg); // 隐式 this
finalArgs.addAll(explicitRegs); // 其它参数
}
// 2. 规范化被调用方法名区分成员方法与普通函数
String callee = switch (call.callee()) {
// 成员方法调用 obj.method()
case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
// 普通函数调用或处理命名空间前缀如当前方法名为 namespace.func
case IdentifierNode id -> {
// ========= 情况 3: 普通函数调用 foo(...) =========
String current = ctx.getFunction().name(); // 当前函数全名
String current = ctx.getFunction().name();
int dot = current.lastIndexOf('.');
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());
if (dot > 0)
yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
yield id.name(); // 全局函数调用
}
// 其它类型不支持
default -> throw new IllegalStateException(
"不支持的调用目标: " + call.callee().getClass().getSimpleName());
};
// 3. 分配目标寄存器生成函数/方法调用指令
// 3. 分配用于存放返回值的新寄存器并生成 Call 指令
IRVirtualRegister dest = ctx.newRegister();
ctx.addInstruction(new CallInstruction(dest, callee, finalArgs));
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
return dest;
}
/**
* 二元表达式构建结果存储到新寄存器
* <br>

View File

@ -12,15 +12,14 @@ import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
/**
* IR 函数构建器
* <p>
* 将语法树中的 {@link FunctionNode} 转换为可执行的 {@link IRFunction}包含
* 负责将语法树中的 FunctionNode 节点转化为可执行的 IRFunction
* 包含参数声明返回类型推断函数体语句转换等步骤
*
* <ul>
* <li>在全局函数表登记函数名与返回类型</li>
* <li>初始化 IR 容器与构建上下文作用域寄存器池等</li>
* <li>导入全局/跨模块常量便于常量折叠与跨模块引用</li>
* <li>依据返回类型为表达式设置数字字面量默认后缀</li>
* <li>分配形参寄存器并注册到作用域与 IR 函数</li>
* <li>将函数体语句逐条转为 IR 指令</li>
* <li>在构建完成后清理默认字面量后缀避免泄漏到其它函数</li>
* <li>支持自动导入全局/跨模块常量使跨模块常量引用 ModuleA.a IR 阶段可用</li>
* <li>将函数形参声明为虚拟寄存器并注册到作用域便于后续指令生成</li>
* <li>根据返回类型设置表达式默认字面量类型保证 IR 层类型一致性</li>
* <li>遍历并转换函数体语句为 IR 指令</li>
* </ul>
*/
public class FunctionBuilder {
@ -45,56 +44,57 @@ public class FunctionBuilder {
* @return 构建得到的 IRFunction 对象
*/
public IRFunction build(FunctionNode functionNode) {
// 1) 在全局函数表登记名称 + 返回类型返回类型可能为 null例如构造函数 init
// 1. 在全局函数表注册函数名与返回类型
// 方便其他阶段/模块调用类型检查
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
// 2) 初始化 IR 容器与上下文
// 2. 初始化 IRFunction 实例与上下文对象
// IRFunction: 表示该函数的中间代码容器
// IRContext: 负责作用域寄存器分配等编译上下文管理
IRFunction irFunction = new IRFunction(functionNode.name());
IRContext irContext = new IRContext(irFunction);
// 3) 导入所有全局/跨模块常量到当前作用域便于常量折叠和跨模块引用
// 3. 自动导入所有全局/跨模块常量到当前作用域
// 支持如 ModuleA.a 这样的常量访问/折叠参见 ExpressionBuilder
GlobalConstTable.all().forEach((k, v) ->
irContext.getScope().importExternalConst(k, v));
// 4) 根据函数返回类型设置数字字面量默认后缀
// - 关键修复 returnType 进行空值/空白保护缺省视为 "void"
String rt = functionNode.returnType();
String rtLower = (rt == null || rt.trim().isEmpty()) ? "void" : rt.trim().toLowerCase();
// 根据返回类型决定默认字面量后缀
// 仅在浮点/整型长短类型上设置其它/void 情况不设置使用 '\0' 表示不设置
char defaultSuffix = switch (rtLower) {
// 4. 根据函数返回类型设置默认类型后缀
// 例如返回类型为 double , 字面量表达式自动用 d 后缀
char _returnSuffix = switch (functionNode.returnType().toLowerCase()) {
case "double" -> 'd';
case "float" -> 'f';
case "long" -> 'l';
case "short" -> 's';
case "byte" -> 'b';
default -> '\0';
default -> '\0'; // 其它类型不设默认后缀
};
ExpressionUtils.setDefaultSuffix(defaultSuffix);
ExpressionUtils.setDefaultSuffix(_returnSuffix);
try {
// 5) 处理形参
// - 为每个参分配一个新的虚拟寄存器 IRContext 统一分配保证作用域一致
// - (参数名, 类型, 寄存器) 声明到当前作用域
// - 将寄存器加入 IRFunction 的参数列表便于后续调用/生成
// 5. 遍历函数参数列表
// - 为每个分配一个新的虚拟寄存器
// - 注册参数名类型寄存器到当前作用域
// - 添加参数寄存器到 IRFunction用于后续调用与指令生成
for (ParameterNode p : functionNode.parameters()) {
IRVirtualRegister reg = irContext.newRegister(); // 使用上下文统一分配
IRVirtualRegister reg = irFunction.newRegister();
irContext.getScope().declare(p.name(), p.type(), reg);
irFunction.addParameter(reg);
}
// 6) 构建函数体将每条语句节点转换为 IR 指令序列
// 6. 遍历函数体语句节点转换为 IR 指令
// StatementBuilder 负责将每条语句递归转换为 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;
}
}

View File

@ -3,7 +3,6 @@ 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;
@ -17,7 +16,6 @@ import java.util.Map;
* <li>支持变量的类型信息记录与查询</li>
* <li>支持变量的编译期常量值记录与查询便于常量折叠等优化</li>
* <li>支持跨模块全局常量 ModuleA.a查找</li>
* <li>维护结构体字段布局全局共享字段名 槽位下标用于对象字段读写</li>
* </ul>
*/
final class IRBuilderScope {
@ -30,23 +28,14 @@ final class IRBuilderScope {
private final Map<String, Object> varConstValues = new HashMap<>();
/**
* 存放跨模块导入的全局常量 ModuleA.a
* 键为 "ModuleA.a"值为常量值
* 额外存放跨模块导入的全局常量
* key 形如 "ModuleA.a" value 为其常量值
*/
private final Map<String, Object> externalConsts = new HashMap<>();
/**
* 结构体字段布局的全局静态表
* 结构体名 (字段名 槽位下标)
* 静态表设计确保所有作用域所有 IR 构建都能访问同一套布局
*/
private static final Map<String, Map<String, Integer>> STRUCT_LAYOUTS = new HashMap<>();
/** 当前作用域所绑定的 IRFunction 实例,用于变量分配新寄存器等。 */
/** 当前作用域所绑定的 IRFunction 实例 */
private IRFunction fn;
// ---------------- 作用域与变量 ----------------
/**
* 绑定当前作用域到指定 IRFunction
*
@ -66,7 +55,7 @@ final class IRBuilderScope {
IRVirtualRegister reg = fn.newRegister();
vars.put(name, reg);
varTypes.put(name, type);
varConstValues.remove(name); // 声明新变量即清除原常量绑定
varConstValues.remove(name);
}
/**
@ -79,7 +68,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);
}
/**
@ -115,18 +104,16 @@ final class IRBuilderScope {
/**
* 获取变量名到类型名映射的不可变副本
*
* @return 变量名类型名映射的只读视图用于调试/全局分析
* @return 变量名类型名映射的只读视图
*/
Map<String, String> getVarTypes() {
return Collections.unmodifiableMap(varTypes);
return Map.copyOf(varTypes);
}
// ---------------- 编译期常量相关接口 ----------------
/**
* 设置变量的编译期常量值本地变量
* <p>
* 便于 IR 生成时做常量折叠等优化value null 则清除绑定
*
* @param name 变量名称
* @param value 常量值null 表示清除
@ -137,9 +124,9 @@ final class IRBuilderScope {
}
/**
* 获取变量的编译期常量值优先本地再查跨模块导入
* <p>
* 常用于优化与折叠支持 "Module.a" 这类跨模块全局常量查找
* 获取变量的编译期常量值本地变量或导入的外部常量
* <br>
* 优先查找本地常量未命中再查外部 "ModuleA.a"
*
* @param name 变量名称或"模块名.常量名"
* @return 编译期常量值 null
@ -147,7 +134,7 @@ final class IRBuilderScope {
Object getConstValue(String name) {
Object v = varConstValues.get(name);
if (v != null) return v;
// 支持跨模块常量/全局变量 "ModuleA.a"
// 支持跨模块常量/全局变量
return externalConsts.get(name);
}
@ -171,60 +158,4 @@ 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<String, Integer> 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<String, Integer> layout = STRUCT_LAYOUTS.get(structName);
// 兼容 模块.结构体 的写法若没命中退化为简单名再查
if (layout == null && structName != null) {
int dot = structName.lastIndexOf('.');
if (dot >= 0 && dot + 1 < structName.length()) {
String simple = structName.substring(dot + 1);
layout = STRUCT_LAYOUTS.get(simple);
}
}
if (layout == null) return null;
return layout.get(fieldName);
}
/**
* 读取某结构体的完整字段布局返回只读 Map
* 支持模块.结构体及简单名两种写法
*
* @param structName 结构体名
* @return 字段名到下标映射的只读视图 null
*/
static Map<String, Integer> getStructLayout(String structName) {
Map<String, Integer> 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);
}
}

View File

@ -6,7 +6,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
/**
* {@code IRContext} 类负责封装当前正在构建的 IRFunction 实例
* IRContext 类负责封装当前正在构建的 IRFunction 实例
* 以及与之配套的作用域管理IRBuilderScope
* 并简化虚拟寄存器分配与 IR 指令添加操作
*
@ -16,92 +16,104 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
* <li>管理变量名与虚拟寄存器的映射关系</li>
* <li>分配新的虚拟寄存器实例</li>
* <li>将生成的 IRInstruction 自动添加到 IRFunction </li>
* <li>支持声明阶段临时类型记录如变量声明时的类型推断/校验</li>
* </ul>
* <b></b> 该类一般不直接暴露给最终用户仅供 IR 构建器内部流程调用
*/
public class IRContext {
/** label 自动生成计数器,用于保证唯一性 */
/* 生成唯一标签用 */
private int labelCounter = 0;
/** 当前正在构建的 IRFunction 对象,所有 IR 指令均将添加至此 */
/**
* 当前正在构建的 IRFunction 对象所有指令将添加至此
*/
private final IRFunction function;
/** 用于管理当前函数作用域内变量与虚拟寄存器、类型等的映射关系 */
/**
* 用于管理当前函数作用域内变量与虚拟寄存器的映射
*/
private final IRBuilderScope scope;
/** 当前 declare 编译阶段变量类型,不在变量声明流程时为 null */
/**
* 当前声明变量的类型不在声明变量时为空
*/
private String varType;
/**
* 构造一个新的 IRContext并将指定的 IRFunction 与作用域管理器关联
* 构造一个新的 IRContext并将指定的 IRFunction 与作用域关联
*
* @param function 要构建的 IRFunction 实例不可为 null
* @param function 要构建的 IRFunction 实例
*/
public IRContext(IRFunction function) {
this.function = function;
this.scope = new IRBuilderScope();
// 作用域需知晓当前 IRFunction以便为变量分配寄存器
// 关联作用域与 IRFunction以便在声明变量时申请寄存器
this.scope.attachFunction(function);
this.varType = null;
}
/** 获取当前正在构建的 IRFunction 对象。 */
/**
* 获取当前正在构建的 IRFunction 对象
*
* @return 当前 IRFunction 实例
*/
public IRFunction getFunction() {
return function;
}
/**
* 获取当前函数的变量与寄存器映射作用域
* <p>包内可见仅限 builder 包内部调用避免被外部滥用</p>
*
* <p>包内可见: 仅限 builder 包内部使用
*
* @return IRBuilderScope 实例
*/
IRBuilderScope getScope() {
return scope;
}
/** 为当前函数分配一个新的虚拟寄存器。 */
/**
* 为当前函数分配一个新的虚拟寄存器
*
* @return 分配到的 IRVirtualRegister 对象
*/
public IRVirtualRegister newRegister() {
return function.newRegister();
}
/**
* 兼容用法分配一个临时虚拟寄存器
* <p>
* 目前直接委托给 {@link #newRegister()}便于老代码兼容与简单用法
* 若将来有命名/临时寄存器区分的需要可在此扩展实现
* </p>
* 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中
*
* @param instr 要添加的 IRInstruction 实例
*/
public IRVirtualRegister newTempRegister() {
return newRegister();
}
/** 将指定的 IRInstruction 添加到当前 IRFunction 的指令列表中。 */
public void addInstruction(IRInstruction instr) {
function.add(instr);
}
/**
* 生成一个唯一标签名 L0L1L2...
* <p>常用于条件跳转分支合流等 IR 控制流构建场景</p>
*
* @return 形如 "L0", "L1" 等的唯一字符串标签
*/
/** 生成一个形如 L0 / L1 ... 的唯一标签名 */
public String newLabel() {
return "L" + (labelCounter++);
}
/** 获取当前 declare 编译阶段变量类型(声明流程中临时记录) */
/**
* 获取当前 declare 编译阶段变量类型
*
* @return 当前 declare 的变量类型
*/
public String getVarType() {
return varType;
}
/** 设置当前 declare 编译阶段变量类型(一般在变量声明时赋值) */
/**
* 设置当前 declare 编译阶段变量类型
*
*/
public void setVarType(String type) {
this.varType = type;
}
/** 清除当前 declare 编译阶段变量类型(声明流程结束时调用) */
/**
* 清除当前 declare 编译阶段变量类型
*
*/
public void clearVarType() {
this.varType = null;
}

View File

@ -9,209 +9,62 @@ 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.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* IRProgramBuilder 负责将 AST 顶层节点如模块函数语句等转换为可执行的 {@link IRProgram}
* IRProgramBuilder 负责将 AST 顶层节点转换为可执行的 {@link IRProgram}
*
* <p>
* 主要工作内容包括
* </p>
* <ul>
* <li>预扫描所有模块 <code>declare const</code> 常量登记到全局常量表支持跨模块常量折叠</li>
* <li>预扫描并注册所有 struct 的字段布局字段下标 IR 阶段对象字段读写使用</li>
* <li>对模块内的普通函数加上模块前缀ModuleName.func保证命名唯一并将本模块全局声明注入到函数体前部</li>
* <li> struct init methods 降级为普通 IR 函数并注册为 <code>StructName.__init__</code><code>StructName.method</code>同时在其参数列表首位插入隐式 <code>this: StructName</code></li>
* <li>对模块内的函数加上模块前缀保证命名唯一并将本模块全局声明注入到函数体前部</li>
* <li>将独立顶层语句自动包装为特殊的 "_start" 函数脚本模式支持</li>
* </ul>
*
* <p>
* 该类为不可变工具类仅包含静态行为不持有状态
* </p>
*/
public final class IRProgramBuilder {
/**
* 将解析生成的 AST 根节点列表转换为 IRProgram
*
* @param roots 顶层 AST 根节点列表ModuleNode / FunctionNode / StatementNode
* @return 构建好的 IRProgram
* @param roots ModuleNodeFunctionNode StatementNode 的顶层 AST 根节点列表
* @return 包含所有转换后 IRFunction IRProgram 对象
* @throws IllegalStateException 遇到不支持的顶层节点类型时抛出
*
* <p>主流程</p>
* <ol>
* <li>登记全局常量便于后续常量折叠</li>
* <li>注册所有 struct 的字段布局为成员读写和 this.xx 做准备</li>
* <li>遍历根节点模块 先降级并注册 struct 的构造/方法再处理模块函数顶层函数直接构建顶层语句打包为 "_start" 构建</li>
* </ol>
*/
public IRProgram buildProgram(List<Node> roots) {
// 1. 先登记全局常量便于后续常量折叠
// 预先收集并登记全部模块常量到全局常量表
preloadGlobals(roots);
// 2. 注册所有结构体的字段布局为成员访问做准备
preloadStructLayouts(roots);
// 创建 IR 程序对象
IRProgram irProgram = new IRProgram();
// 3. 遍历并处理所有顶层节点
for (Node node : roots) {
switch (node) {
case ModuleNode moduleNode -> {
// 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 ->
// 3.3 处理顶层函数节点直接构建为 IRFunction 并加入
// 处理顶层函数节点直接构建为 IRFunction 并加入
irProgram.add(buildFunction(functionNode));
case StatementNode statementNode ->
// 3.4 处理脚本式顶层语句封装成 "_start" 函数后构建并添加
// 处理脚本式顶层语句封装成 "_start" 函数后构建并添加
irProgram.add(buildFunction(wrapTopLevel(statementNode)));
default ->
// 3.5 遇到未知类型节点抛出异常
// 遇到未知类型节点抛出异常
throw new IllegalStateException("Unsupported top-level node: " + node);
}
}
return irProgram;
}
// ===================== 预扫描注册结构体字段布局 =====================
// ===================== 全局常量收集 =====================
/**
* 为每个结构体注册字段布局字段名 槽位索引并在有继承时将父类布局复制并续接
*
* <p>规则</p>
* <ol>
* <li>若存在父类先取到父类布局如果能找到将其按顺序复制到当前布局中起始索引 = 父类字段数</li>
* <li>再将当前结构体声明的字段按声明顺序追加如果字段名与父类重复跳过以避免覆盖</li>
* <li>最后将布局以 <code>StructName</code> 为键注册到 {@link IRBuilderScope} 的全局布局表</li>
* </ol>
* @param roots AST 顶层节点列表包含模块/结构体信息
*/
private void preloadStructLayouts(List<Node> roots) {
for (Node n : roots) {
if (!(n instanceof ModuleNode mod)) continue;
if (mod.structs() == null) continue;
for (StructNode s : mod.structs()) {
List<DeclarationNode> fields = s.fields();
if (fields == null) continue;
Map<String, Integer> layout = new LinkedHashMap<>();
int idx = 0;
// 1. 若有父类先复制父类布局并将索引起点置为父类字段数
String parentName = s.parent();
if (parentName != null && !parentName.isBlank()) {
// 约定IRBuilderScope 内部维护一个结构体名 字段布局的全局表
// 这里假定提供静态查询方法 getStructLayout(String)
Map<String, Integer> parentLayout = IRBuilderScope.getStructLayout(parentName);
if (parentLayout != null && !parentLayout.isEmpty()) {
layout.putAll(parentLayout);
idx = parentLayout.size();
}
}
// 2. 续接当前结构体声明的字段若与父类同名跳过避免覆盖父类槽位
for (DeclarationNode d : fields) {
String name = d.getName();
if (!layout.containsKey(name)) {
layout.put(name, idx++);
}
}
// 3. 注册 Person {name:0}Student(extends Person) {name:0, studentId:1}
IRBuilderScope.registerStructLayout(s.name(), layout);
}
}
}
// ===================== Struct 降级方法/构造 普通函数 =====================
/**
* 将一个 Struct 的所有构造函数inits和方法methods降级为普通 Function并注册进 IRProgram
* <ul>
* <li>构造函数StructName.__init__N(this:StructName, ...)N为参数个数</li>
* <li>方法StructName.method(this:StructName, ...)</li>
* </ul>
* <p>降级规则</p>
* <ol>
* <li>构造/方法函数名前缀加上结构体名便于唯一定位</li>
* <li>参数列表最前添加隐式 this:StructName 参数</li>
* </ol>
*
* @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<ParameterNode> 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 全局变量即编译期常量
* 扫描所有模块节点将其中声明的 const 全局变量compile-time 常量
* "模块名.常量名" 形式注册到全局常量表
* <p>
* 支持跨模块常量折叠便于 IR 生成时做常量传播与优化
* </p>
* 支持跨模块常量折叠用于后续 IR 生成过程中的常量折叠优化
*
* @param roots AST 顶层节点列表
*/
@ -221,7 +74,7 @@ public final class IRProgramBuilder {
String moduleName = mod.name();
if (mod.globals() == null) continue;
for (DeclarationNode decl : mod.globals()) {
// 只处理带初始值的 const 常量编译期常量忽略 run-time/初始值
// 只处理 compile-time const 常量并要求有初始值
if (!decl.isConst() || decl.getInitializer().isEmpty()) continue;
ExpressionNode init = decl.getInitializer().get();
Object value = evalLiteral(init);
@ -235,28 +88,24 @@ public final class IRProgramBuilder {
/**
* 字面量提取与类型折叠工具
* <p>
* 用于将表达式节点还原为 Java 原生类型intlongdoubleString等仅支持直接字面量
* 不支持复杂表达式非常量等情况无法静态折叠则返回 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);
@ -270,9 +119,9 @@ public final class IRProgramBuilder {
yield null;
}
}
case StringLiteralNode str -> str.value(); // 字符串字面量直接返回
case BoolLiteralNode b -> b.getValue() ? 1 : 0; // 布尔常量转为 1/0
default -> null; // 其他情况不支持常量折叠
case StringLiteralNode str -> str.value();
case BoolLiteralNode b -> b.getValue() ? 1 : 0;
default -> null;
};
}
@ -280,23 +129,21 @@ public final class IRProgramBuilder {
/**
* 构建带有模块全局声明注入的函数并将函数名加上模块前缀保证模块内函数名唯一
* <p>
* 如果模块有全局声明则这些声明会被插入到函数体前部<b>会过滤掉与参数同名的全局声明</b>防止变量遮蔽
* </p>
* 如果模块有全局声明则这些声明会被插入到函数体前部**会过滤掉与参数同名的全局声明**
*
* @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<String> paramNames = new HashSet<>();
for (ParameterNode p : functionNode.parameters()) {
paramNames.add(p.name());
@ -309,12 +156,12 @@ public final class IRProgramBuilder {
}
}
// 4. 若无可插入的全局声明直接重命名构建
if (filteredGlobals.isEmpty()) {
// 过滤后已无可插入的全局声明
return buildFunction(renameFunction(functionNode, qualifiedName));
}
// 5. 合并全局声明与函数体前插全局声明
// 合并全局声明与函数体前插全局声明
List<StatementNode> newBody = new ArrayList<>(filteredGlobals.size() + functionNode.body().size());
newBody.addAll(filteredGlobals);
newBody.addAll(functionNode.body());
@ -350,8 +197,6 @@ public final class IRProgramBuilder {
*
* @param functionNode 待构建的 FunctionNode
* @return 构建后的 IRFunction
*
* <p>本方法仅作中转直接委托给 FunctionBuilder</p>
*/
private IRFunction buildFunction(FunctionNode functionNode) {
return new FunctionBuilder().build(functionNode);
@ -370,7 +215,6 @@ public final class IRProgramBuilder {
List.of(),
"void",
List.of(stmt),
// (-1,-1,"")占位避免依赖真实位置信息
new NodeContext(-1, -1, "")
);
}

View File

@ -104,7 +104,7 @@ public class InstructionFactory {
/**
* 生成值拷贝语义src dest
* 通过在 IR 中构造 src + 0 的形式触发 Peephole 优化折叠成 MOV
* 若类型无法推断默认采用 int 方案ADD_I32, src+0
*
* @param ctx 当前 IR 上下文
* @param src 源寄存器
@ -114,50 +114,46 @@ public class InstructionFactory {
if (src == dest) {
return;
}
String varType = ctx.getVarType();
IROpCode op;
IRConstant zeroConst;
String varType = ctx.getVarType(); // 需要 IRContext 提供
char suffix = '\0';
if (varType != null) {
switch (varType) {
case "byte" -> {
op = IROpCode.ADD_B8;
zeroConst = new IRConstant((byte) 0);
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 "short" -> {
op = IROpCode.ADD_S16;
zeroConst = new IRConstant((short) 0);
}
case "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 "long" -> {
op = IROpCode.ADD_L64;
zeroConst = new IRConstant(0L);
case 'f' -> {
zero = loadConst(ctx, 0.0f);
yield IROpCode.ADD_F32;
}
case "float" -> {
op = IROpCode.ADD_F32;
zeroConst = new IRConstant(0.0f);
case 'l' -> {
zero = loadConst(ctx, 0L);
yield IROpCode.ADD_L64;
}
case "double" -> {
op = IROpCode.ADD_D64;
zeroConst = new IRConstant(0.0);
case 's' -> {
zero = loadConst(ctx, 0);
yield IROpCode.ADD_S16;
}
case 'b' -> {
zero = loadConst(ctx, 0);
yield IROpCode.ADD_B8;
}
default -> {
// 引用类型 / 结构体等统一走 int 路径
op = IROpCode.ADD_I32;
zeroConst = new IRConstant(0);
zero = loadConst(ctx, 0);
yield IROpCode.ADD_I32;
}
}
} else {
// 无法推断类型时退化为 int 方案
op = IROpCode.ADD_I32;
zeroConst = new IRConstant(0);
}
// 注意这里传入的是立即数 zeroConst而不是寄存器
ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zeroConst));
};
ctx.addInstruction(new BinaryOperationInstruction(op, dest, src, zero));
}
/**

View File

@ -24,6 +24,9 @@ import java.util.ArrayDeque;
* <li>负责控制流跳转分支循环的标签分配与维护</li>
* <li>在变量赋值和声明时自动常量折叠和登记</li>
* </ul>
*
* @author [你的名字]
* @since 1.0
*/
public class StatementBuilder {
@ -81,58 +84,8 @@ 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.<field> 的结构体字段赋值
IRVirtualRegister thisReg = ctx.getScope().lookup("this");
String thisType = ctx.getScope().lookupType("this");
if (thisReg != null && thisType != null) {
java.util.Map<String, Integer> 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<org.jcnc.snow.compiler.ir.core.IRValue> 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);
@ -144,7 +97,8 @@ public class StatementBuilder {
ctx.getScope().setConstValue(var, constVal);
else
ctx.getScope().clearConstValue(var);
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
ctx.clearVarType();
return;
@ -209,9 +163,11 @@ public class StatementBuilder {
ctx.setVarType(decl.getType());
// 2. 为当前声明的变量分配一个全新的虚拟寄存器
// 这样可以保证该变量和初始值表达式中的变量物理上独立不会发生别名/串扰
IRVirtualRegister dest = ctx.newRegister();
// 3. 将初始值表达式的计算结果写入新分配的寄存器
// 即使初始值是某个已存在变量 outer_i这里是值的拷贝
expr.buildInto(decl.getInitializer().get(), dest);
// 声明赋初值时登记常量
@ -221,7 +177,8 @@ 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);
@ -231,18 +188,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()) {
@ -251,7 +208,6 @@ public class StatementBuilder {
InstructionFactory.jmp(ctx, breakTargets.peek());
return;
}
if (stmt instanceof ContinueNode) {
// continue 语句跳转到当前最近一层循环的 step 起始标签
if (continueTargets.isEmpty()) {
@ -260,12 +216,10 @@ public class StatementBuilder {
InstructionFactory.jmp(ctx, continueTargets.peek());
return;
}
// 不支持的语句类型
throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt);
}
/**
* 获取变量名对应的寄存器如果尚未声明则新声明一个并返回
*

View File

@ -3,99 +3,29 @@ package org.jcnc.snow.compiler.ir.value;
import org.jcnc.snow.compiler.ir.core.IRValue;
/**
* {@code IRConstant} 表示中间表示IR系统中的常量值节点
* IRConstant 表示中间表示IR系统中的常量值
* <p>
* 常量用于表示在编译期间已知的不可变值例如字面整数浮点数布尔值或字符串等
* {@link IRVirtualRegister} 不同IRConstant 不需要通过寄存器存储
* 可直接作为 IR 指令的操作数立即数/常量池引用等
* 常量用于表示在编译期间已知的不可变值例如字面整数浮点数布尔值或字符串
* {@link IRVirtualRegister} 不同常量不需要通过寄存器存储
* 可直接作为 IR 指令的操作数使用
* <p>
* 典型应用:
* - 加载常量指令: v1 = CONST 42
* - 计算表达式: v2 = ADD v1, 100
*/
public record IRConstant(Object value) implements IRValue {
/**
* 通用工厂方法将任意 Java 对象包装为 IRConstant
*
* @param v 任意对象
* @return IRConstant 封装后的常量
*/
public static IRConstant fromObject(Object v) {
return new IRConstant(v);
}
/**
* 数字字面量工厂接受源码字面量字符串解析为 int/long/double Java 数值类型
* 将常量值转换为字符串用于打印 IR 指令或调试输出
* <p>
* 支持下划线分隔 "1_000_000"类型后缀 'l','d','f' 并自动转换为
* <ul>
* <li>double带小数点或科学计数法</li>
* <li>long/byte/short/int依类型后缀或值范围判定</li>
* <li>解析失败时兜底为字符串原文保证编译不中断</li>
* </ul>
* 例如:
* - 整数常量: 42
* - 字符串常量: "hello"
*
* @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);
}
/**
* 布尔字面量工厂
*
* @param v 布尔值
* @return IRConstant 封装的布尔常量
*/
public static IRConstant fromBoolean(boolean v) {
return new IRConstant(v);
}
/**
* 调试友好的字符串表示
* <ul>
* <li>字符串常量输出带引号转义引号和反斜杠</li>
* <li>null 输出为 "null"</li>
* <li>其它类型调用 toString()</li>
* </ul>
* @return 常量的字符串表示
*/
@Override
public String toString() {
if (value == null) return "null";
if (value instanceof String s) {
String escaped = s.replace("\\", "\\\\").replace("\"", "\\\"");
return "\"" + escaped + "\"";
}
return String.valueOf(value);
return value.toString();
}
}

View File

@ -1,11 +1,9 @@
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;
@ -56,7 +54,6 @@ public class LexerEngine {
/* 2. 后置整体校验 */
validateTokens();
/* 3. 打印 token */
// TODO: TokenPrinter
// if (SnowConfig.isDebug()) {
// TokenPrinter.print(tokens);
// }

View File

@ -28,9 +28,7 @@ public class TokenFactory {
private static final Set<String> KEYWORDS = Set.of
("module", "function", "params", "returns", "body", "end",
"if", "then", "else", "loop", "declare", "return", "import", "init",
"cond", "step", "globals", "break", "continue", "const",
"new", "extends"
);
"cond", "step", "globals", "break", "continue", "const");
/**
* 内置类型名称集合 intstring

View File

@ -7,62 +7,44 @@ import java.util.List;
import java.util.StringJoiner;
/**
* {@code ModuleNode}
* <p>
* 抽象语法树AST顶层节点 模块定义
* <ul>
* <li>代表一个完整的源码模块/文件 main, math </li>
* <li>包含模块名导入列表全局变量结构体和函数定义等全部模块级内容</li>
* <li>是整个编译流程的入口节点</li>
* </ul>
* </p>
* 表示模块定义的 AST 节点
* 一个模块通常由模块名导入语句列表和函数定义列表组成
*
* @param name 模块名称
* @param imports 导入模块列表
* @param globals 全局变量/常量声明
* @param structs 结构体定义列表
* @param functions 函数定义列表
* @param context 源代码位置信息
* @param name 模块名称
* @param imports 模块导入列表每个导入是一个 {@link ImportNode}
* @param functions 模块中的函数列表每个函数是一个 {@link FunctionNode}
* @param context 节点上下文信息包含行号列号等
*/
public record ModuleNode(
String name,
List<ImportNode> imports,
List<DeclarationNode> globals,
List<StructNode> structs,
List<FunctionNode> functions,
NodeContext context
) implements Node {
/**
* 返回模块节点的简要字符串表示用于日志调试
* 列出模块名导入全局变量结构体函数等简明内容
* 返回模块节点的字符串表示形式包含模块名导入模块列表和函数列表
*
* @return 字符串形式
* Module(name=main, imports=[math], globals=[int x], structs=[Foo], functions=[bar])
* @return 模块的简洁字符串表示用于调试和日志输出
*/
@Override
public String toString() {
// 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 + "])";
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 + "])";
}
}

View File

@ -1,50 +0,0 @@
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}
* <p>
* 抽象语法树AST节点 对象创建表达式
* <pre>new TypeName(arg1, arg2, ...)</pre>
*
* <p>
* 语义在源码中通过 <b>new</b> 关键字实例化某个类型
* 实参列表为构造函数参数表达式类型为对应结构体类型
* </p>
*
* <p>
* 字段说明
* <ul>
* <li>{@code typeName} 创建对象的目标类型名 "Person""MyStruct"</li>
* <li>{@code arguments} 构造函数参数表达式列表可为空</li>
* <li>{@code ctx} 源码位置上下文行列信息便于报错和调试定位</li>
* </ul>
* </p>
*
* @param typeName 目标类型名
* @param arguments 构造参数表达式列表
* @param ctx 源代码位置信息
*/
public record NewExpressionNode(
String typeName,
List<ExpressionNode> arguments,
NodeContext ctx
) implements ExpressionNode {
/**
* 获取本节点的源代码位置信息行号列号等
* <p>
* 实现 ExpressionNode 接口的抽象方法便于统一错误处理与调试
* </p>
*
* @return 当前节点的源代码上下文信息
*/
@Override
public NodeContext context() {
return ctx;
}
}

View File

@ -1,52 +0,0 @@
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}
* <p>
* AST 节点 结构体定义struct
* </p>
*
* <p>
* 描述一个结构体类型包括字段可选构造函数方法列表等
* 结构体可声明零个或多个字段可选构造函数init
* 以及零个或多个方法
*
* @param name 结构体名称
* @param parent 父类名称无继承时为 {@code null}
* @param fields 字段声明列表
* @param inits 构造函数可为 null
* @param methods 方法列表
* @param context 源码位置信息
*/
public record StructNode(
String name,
String parent,
List<DeclarationNode> fields,
List<FunctionNode> inits,
List<FunctionNode> 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();
}
}

View File

@ -1,81 +0,0 @@
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 根节点
*
* <p>
* 用于描述一个完整模块的全部语法内容包括模块名导入依赖全局变量结构体定义函数定义等
* 作为语法树的顶层节点供后续语义分析与 IR 生成统一处理
* </p>
*
* <p><b>字段说明</b></p>
* <ul>
* <li>{@code name} 模块名称通常对应文件名或逻辑包名要求唯一</li>
* <li>{@code imports} import 导入列表描述本模块依赖的其它模块名</li>
* <li>{@code globals} 全局变量与常量声明列表作用域为整个模块</li>
* <li>{@code structs} 结构体定义列表支持零个或多个结构体</li>
* <li>{@code functions} 函数定义列表支持零个或多个函数</li>
* <li>{@code context} 源代码位置信息用于报错定位与调试</li>
* </ul>
*
* <p>
* 该类为 Java record天然不可变构造参数即为字段名调用时自动生成 getter 方法
* </p>
*
* @param name 模块名称 "main"
* @param imports 导入依赖列表ImportNode
* @param globals 全局变量声明列表DeclarationNode
* @param structs 结构体定义列表StructNode
* @param functions 函数定义列表FunctionNode
* @param context 源代码位置信息NodeContext
*/
public record ModuleNode(
String name,
List<ImportNode> imports,
List<DeclarationNode> globals,
List<StructNode> structs,
List<FunctionNode> 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 + "])";
}
}

View File

@ -1,84 +0,0 @@
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}
* <p>
* 解析对象创建表达式 <code>new TypeName(arg1, arg2, ...)</code> 的前缀解析器实现
* <ul>
* <li>用于 Pratt 解析框架实现 new 关键字前缀的自定义解析逻辑</li>
* <li>支持类型名为内置类型TokenType.TYPE或用户结构体名TokenType.IDENTIFIER</li>
* <li>支持可变参数列表参数为任意表达式</li>
* </ul>
* </p>
*/
public class NewObjectParselet implements PrefixParselet {
/**
* 解析 new 对象创建表达式并生成 AST 新建节点
*
* @param ctx 语法分析上下文包含 TokenStream 及源文件名等信息
* @param token 当前读取到的 new 关键字 Token
* @return 表达式节点NewExpressionNode
*
* <p>
* 解析流程
* <ol>
* <li>读取类型名支持内建类型或结构体名</li>
* <li>读取参数列表参数为表达式用逗号分隔</li>
* <li>封装为 NewExpressionNode记录源码位置信息</li>
* </ol>
* 出错时会抛出 UnexpectedToken 异常
* </p>
*/
@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<ExpressionNode> 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);
}
}

View File

@ -13,62 +13,62 @@ import java.util.HashMap;
import java.util.Map;
/**
* {@code PrattExpressionParser}
* {@code PrattExpressionParser} 基于 Pratt 算法的表达式解析器实现
* <p>
* 基于 Pratt 算法的表达式解析器经典运算符优先级递归解析框架
* <ul>
* <li>支持注册前缀和中缀解析器灵活组合表达式语法</li>
* <li>支持字面量变量函数调用成员访问对象创建各类一元/二元/多元运算</li>
* <li>可快速扩展新语法只需注册新的 Parselet 即可</li>
* <li>出错时统一抛出 {@link UnsupportedFeature}便于调试和错误提示</li>
* </ul>
* 该类通过前缀PrefixParselet和中缀InfixParselet解析器注册表
* 支持灵活扩展的表达式语法包括字面量变量函数调用成员访问和各种运算符表达式
* </p>
* <p>
* 运算符优先级通过枚举控制结合递归解析实现高效的优先级处理和语法结构解析
* 未注册的语法类型或运算符会统一抛出 {@link UnsupportedFeature} 异常
* </p>
*/
public class PrattExpressionParser implements ExpressionParser {
/**
* 前缀解析器注册表通过 Token 类型名或词素作为索引
* <ul>
* <li>如数字字面量标识符字符串布尔值new分组数组一元运算等</li>
* <li>支持同时用 TokenType 名称和具体词素 "(""-"注册</li>
* </ul>
* 前缀解析器注册表 Token 类型名或词素索引
* <p>
* 用于存储所有支持的前缀表达式解析器例如字面量变量分组数组一元运算等
* 支持通过 TokenType 的名称和特定词素 "(", "["两种方式索引
* </p>
*/
private static final Map<String, PrefixParselet> prefixes = new HashMap<>();
/**
* 中缀解析器注册表通过运算符词素索引
* <ul>
* <li> + - * / % 及比较逻辑函数调用下标成员访问等</li>
* <li>词素索引 "+""-""(""."</li>
* </ul>
* 中缀解析器注册表运算符词素索引
* <p>
* 用于存储所有支持的中缀表达式解析器如二元运算函数调用下标成员访问等
* 通过词素索引 "+", "-", "(", "["
* </p>
*/
private static final Map<String, InfixParselet> 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("(", 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());
}
/**
* 统一表达式解析入口自动以最低优先级递归解析整个表达式
* 解析任意表达式的统一入口
* <p>
* 能解析嵌套复合等所有合法表达式结构
* 该方法将以最低优先级启动表达式递归解析能够自动适配和处理多层嵌套或复杂组合表达式
* </p>
*
* @param ctx 当前解析上下文对象 token 流等信息
* @param ctx 当前解析上下文对象持有 token 流等信息
* @return 解析得到的表达式 AST 节点对象
*/
@Override
@ -106,42 +106,50 @@ public class PrattExpressionParser implements ExpressionParser {
}
/**
* Pratt 算法主递归循环按给定优先级递归解析表达式
* 按指定优先级解析表达式Pratt 算法核心
* <p>
* 实现按优先级吸收中缀操作符如连续算术链式调用组合表达式等
* 1. 先取当前 token查找对应的前缀解析器进行初始解析构建表达式左侧如字面量变量等
* 2. 然后循环检测是否有更高优先级的中缀操作符
* 若有则递归处理右侧表达式并组合为新的表达式节点
* </p>
* <p>
* 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常
* </p>
*
* @param ctx 解析上下文
* @param prec 当前已绑定优先级
* @return 已解析的表达式节点
* @param prec 当前运算符优先级用于控制递归层级
* @return 解析构建好的表达式节点
* @throws UnsupportedFeature 遇到未注册的解析器时抛出
*/
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
// 1) 消耗一个 token 作为前缀起点
// 取下一个 token 作为本轮前缀表达式起始
Token token = ctx.getTokens().next();
// 2) 查找前缀解析器优先按词素再按 TokenType
PrefixParselet prefix = prefixes.get(token.getLexeme());
// 查找前缀解析器先按类型名再按词素
PrefixParselet prefix = prefixes.get(token.getType().name());
if (prefix == null) {
prefix = prefixes.get(token.getType().name());
prefix = prefixes.get(token.getLexeme());
}
if (prefix == null) {
// 注册前缀解析器直接报错
// 找到前缀解析器则报错
throw new UnsupportedFeature(
"没有为该 Token 注册前缀解析器: " + token.getLexeme() + " / " + token.getType(),
"没有为该 Token 类型注册前缀解析器: " + token.getType(),
token.getLine(),
token.getCol()
);
}
// 3) 前缀解析得到左侧表达式
// 执行前缀解析获得左侧表达式
ExpressionNode left = prefix.parse(ctx, token);
// 4) 主循环不断吸收更高优先级的中缀操作直到优先级不再提升
while (!ctx.getTokens().isAtEnd() && prec.ordinal() < nextPrecedence(ctx)) {
// 不断尝试查找优先级更高的中缀运算符递归处理表达式链
while (!ctx.getTokens().isAtEnd()
&& prec.ordinal() < nextPrecedence(ctx)) {
// 查看下一个 token 词素查找中缀解析器
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 + "'",
@ -149,21 +157,21 @@ public class PrattExpressionParser implements ExpressionParser {
t.getCol()
);
}
// 递归组合更高优先级的中缀表达式
// 使用中缀解析器处理表达式组合
left = infix.parse(ctx, left);
}
// 5) 返回本层解析完成的表达式节点
// 返回本层递归已解析的表达式节点
return left;
}
/**
* 获取下一个 token 对应的中缀运算符优先级Pratt 算法关键
* 获取下一个 token 词素对应的中缀运算符优先级Pratt 算法关键
* <p>
* 若无注册的中缀解析器则返回 -1
* 用于决定当前是否需要递归处理更高优先级的中缀操作
* </p>
*
* @param ctx 解析上下文
* @return 下一个运算符优先级序号无则-1
* @param ctx 当前解析上下文
* @return 下一个中缀运算符的优先级序号若无注册解析器则返回 -1
*/
private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());

View File

@ -62,38 +62,10 @@ 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<ParameterNode> parameters, String returnType, List<StatementNode> body,
NodeContext _

View File

@ -2,16 +2,15 @@ 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.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.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.ParameterNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
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;
@ -23,7 +22,9 @@ import java.util.*;
* 实现 {@link TopLevelParser} 接口用于将源代码中的函数块解析为抽象语法树AST中的 {@link FunctionNode}
*
* <p>
* 使用 {@link FlexibleSectionParser} 机制按照语义区块结构对函数进行模块化解析支持以下部分:
* 本类使用 {@link FlexibleSectionParser} 机制按照语义区块结构对函数进行模块化解析支持以下部分:
* </p>
*
* <ul>
* <li>函数头关键字 {@code function:} 与函数名</li>
* <li>参数列表params 区块</li>
@ -46,80 +47,76 @@ public class FunctionParser implements TopLevelParser {
* 顶层语法解析入口
*
* <p>
* 解析并生成函数的 AST
* 该方法从源代码中获取 {@link TokenStream}并按照函数定义的不同区块解析最终生成 {@link FunctionNode}
* 该方法负责完整解析函数定义包括其所有组成部分并构建对应的 {@link FunctionNode}
* </p>
*
* @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();
int line = ts.peek().getLine();
int column = ts.peek().getCol();
// 记录当前 token 的行号列号和源文件名用于错误报告或生成节点上下文
int line = ctx.getTokens().peek().getLine();
int column = ctx.getTokens().peek().getCol();
String file = ctx.getSourceName();
// 解析函数头 (function:)
// 解析函数头例如可能是 `function` 关键字等
parseFunctionHeader(ts);
// 函数名
// 解析函数名
String functionName = parseFunctionName(ts);
// 用于存放解析出来的参数列表
List<ParameterNode> parameters = new ArrayList<>();
String[] returnType = new String[1]; // 使用数组模拟可变引用
// 用于存放返回类型这里用数组是为了在闭包中修改其值
String[] returnType = new String[1];
// 用于存放函数体语句列表
List<StatementNode> body = new ArrayList<>();
// 定义函数可选区块规则 (params, returns, body)
// 获取函数可以包含的可选节如参数返回类型主体等的定义映射
Map<String, SectionDefinition> sections = getSectionDefinitions(parameters, returnType, body);
// 解析这些区块
// 调用通用的多节解析器实际根据 sections 中注册的规则解析各部分内容
FlexibleSectionParser.parse(ctx, ts, sections);
// 如果函数体为空且返回 void补充一个空 return
if (body.isEmpty() && "void".equals(returnType[0])) {
// 如果函数体为空且返回类型为 void自动补充一个空 return 语句
if (body.isEmpty() && returnType[0].equals("void")) {
body.add(new ReturnNode(null, new NodeContext(line, column, file)));
}
// 检查参数名是否重复
Set<String> 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<String> 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.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 ----------------------- */
/* ====================================================================== */
/**
* 定义函数的各个语义区块及其解析规则包括参数列表返回类型及函数体
* 构造函数定义中各区块的解析规则paramsreturnsbody
*
* <p>
* 此方法将定义如下的三个区块
* <ul>
* <li>"params" 区块解析参数列表</li>
* <li>"returns" 区块解析返回类型</li>
* <li>"body" 区块解析函数体</li>
* </ul>
* 每个 {@link SectionDefinition} 包含两个部分: 区块起始判断器基于关键字与具体的解析逻辑
* </p>
*
* @param params 存储函数参数列表的集合
* @param returnType 存储函数返回类型的字符串数组
* @param body 存储函数体语句列表
* @return 各个区块的解析定义
* @param params 参数节点收集容器解析结果将存入此列表
* @param returnType 返回类型容器以单元素数组方式模拟引用传递
* @param body 函数体语句节点列表容器
* @return 区块关键字到解析定义的映射表
*/
private Map<String, SectionDefinition> getSectionDefinitions(
List<ParameterNode> params,
@ -145,12 +142,8 @@ public class FunctionParser implements TopLevelParser {
return map;
}
/* ====================================================================== */
/* ---------------------------- 函数头/尾 ------------------------------- */
/* ====================================================================== */
/**
* 解析函数头部期望的格式为 `function:`
* 解析函数头部标识符 {@code function:}并跳过其后多余注释与空行
*
* @param ts 当前使用的 {@link TokenStream}
*/
@ -165,7 +158,7 @@ public class FunctionParser implements TopLevelParser {
* 解析函数名称标识符并跳过换行
*
* @param ts 当前使用的 {@link TokenStream}
* @return 函数名字符串
* @return 函数名字符串
*/
private String parseFunctionName(TokenStream ts) {
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
@ -174,7 +167,7 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析函数结束标记 `end function`
* 解析函数结束标记 {@code end function}
*
* @param ts 当前使用的 {@link TokenStream}
*/
@ -184,14 +177,18 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析函数参数列表支持附加注释格式为
* 解析函数参数列表
*
* <p>
* 支持声明后附加注释格式示例:
* <pre>
* params:
* declare x: int // 说明文字
* declare y: float
* </pre>
* </p>
*
* @param ctx 当前解析上下文包含 {@link TokenStream} 和符号表等信息
* @param ctx 当前解析上下文包含 {@link TokenStream} 和符号表等作用域信息
* @return 所有参数节点的列表
*/
private List<ParameterNode> parseParameters(ParserContext ctx) {
@ -211,28 +208,19 @@ 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;
}
int line = ts.peek().getLine();
int column = ts.peek().getCol();
// 获取当前 token 的行号列号和文件名
int line = ctx.getTokens().peek().getLine();
int column = ctx.getTokens().peek().getCol();
String file = ctx.getSourceName();
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());
}
String ptype = ts.expectType(TokenType.TYPE).getLexeme();
skipComments(ts);
ts.expectType(TokenType.NEWLINE);
list.add(new ParameterNode(pname, ptype, new NodeContext(line, column, file)));
@ -241,27 +229,20 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析返回类型声明格式为 `returns: TYPE`支持注释
* 解析返回类型声明
*
* <p>
* 格式为 {@code returns: TYPE}支持前置或行尾注释
* </p>
*
* @param ts 当前使用的 {@link TokenStream}
* @return 返回类型字符串
* @return 返回类型名称字符串
*/
private String parseReturnType(TokenStream ts) {
ts.expect("returns");
ts.expect(":");
skipComments(ts);
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());
}
Token typeToken = ts.expectType(TokenType.TYPE);
String rtype = typeToken.getLexeme();
skipComments(ts);
ts.expectType(TokenType.NEWLINE);
@ -270,7 +251,11 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析函数体区块直到遇到 `end body`
* 解析函数体区块直到遇到 {@code end body}
*
* <p>
* 每一行由对应的语句解析器处理可嵌套控制结构返回语句表达式等
* </p>
*
* @param ctx 当前解析上下文
* @param ts 当前使用的 {@link TokenStream}
@ -290,8 +275,9 @@ 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");
@ -307,7 +293,9 @@ 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();
}
}
/**
@ -316,6 +304,8 @@ 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();
}
}
}

View File

@ -1,7 +1,10 @@
package org.jcnc.snow.compiler.parser.module;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.*;
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.base.NodeContext;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
import org.jcnc.snow.compiler.parser.context.ParserContext;
@ -9,33 +12,47 @@ 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} 负责解析源码中的模块结构是顶层结构解析器实现之一
* <p>
* 顶层结构解析器负责解析整个源码模块module ... end module
* <ul>
* <li>支持模块声明导入(import)全局变量(globals)结构体(struct)函数(function)等顶层语法</li>
* <li>允许模块体中出现任意数量的空行自动跳过顺序自由</li>
* <li>遇到非法顶层语句或区块会抛出 {@link UnexpectedToken}提示具体位置和原因</li>
* </ul>
* 模块定义可包含多个导入import语句globals 全局声明和函数定义function
* 导入语句可在模块中任意位置出现且允许模块体中穿插任意数量的空行空行会被自动忽略不影响语法结构
* </p>
*
* <p>
* 典型模块语法结构:
* <pre>
* module: mymod
* import ...
* globals:
* declare ...
* function ...
* ...
* end module
* </pre>
* </p>
*/
public class ModuleParser implements TopLevelParser {
/**
* 解析一个完整的模块定义块返回 AST {@link ModuleNode}
* 解析一个模块定义块返回完整的 {@link ModuleNode} 语法树节点
* <p>
* 支持空行允许导入全局结构体函数等多种区块混排
* 解析过程包括:
* <ol>
* <li>匹配模块声明起始 {@code module: IDENTIFIER}</li>
* <li>收集模块体内所有 importglobals function 语句允许穿插空行</li>
* <li>匹配模块结束 {@code end module}</li>
* </ol>
* 若遇到未识别的语句将抛出 {@link UnexpectedToken} 异常定位错误位置和原因
* </p>
*
* @param ctx 解析上下文包含 TokenStream文件名等信息
* @return 解析生成的 ModuleNode
* @throws UnexpectedToken 当模块体中遇到不支持的顶层语句时抛出
* @param ctx 当前解析上下文包含词法流等状态
* @return 解析得到的 {@link ModuleNode} 实例
* @throws UnexpectedToken 当模块体中出现未识别的顶层语句时抛出
*/
@Override
public ModuleNode parse(ParserContext ctx) {
@ -45,53 +62,37 @@ 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<StructNode> structs = new ArrayList<>();
List<ImportNode> imports = new ArrayList<>();
List<DeclarationNode> globals = new ArrayList<>();
List<FunctionNode> 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;
@ -99,13 +100,9 @@ public class ModuleParser implements TopLevelParser {
String innerLex = ts.peek().getLexeme();
if ("declare".equals(innerLex)) {
globals.add(globalsParser.parse(ctx));
}
// 下一个 function/import/end 开头则结束 globals 区块
else if ("function".equals(innerLex) || "import".equals(innerLex) || "end".equals(innerLex)) {
} else if ("function".equals(innerLex) || "import".equals(innerLex) || "end".equals(innerLex)) {
break;
}
// 其余标记为非法内容抛出异常
else {
} else {
throw new UnexpectedToken(
"globals 区块中不支持的内容: " + innerLex,
ts.peek().getLine(),
@ -114,7 +111,6 @@ public class ModuleParser implements TopLevelParser {
}
}
}
// 未知或非法顶层内容
case null, default -> throw new UnexpectedToken(
"Unexpected token in module: " + lex,
ts.peek().getLine(),
@ -123,11 +119,9 @@ public class ModuleParser implements TopLevelParser {
}
}
// 5) 匹配模块结尾 "end module"
ts.expect("end");
ts.expect("module");
// 6) 构造并返回 ModuleNode
return new ModuleNode(name, imports, globals, structs, functions, new NodeContext(line, column, file));
return new ModuleNode(name, imports, globals, functions, new NodeContext(line, column, file));
}
}

View File

@ -10,96 +10,75 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
* {@code DeclarationStatementParser} 负责解析变量声明语句节点
* <p>
* <ul>
* <li>支持类型标识符与自定义结构体名</li>
* <li>支持多维数组类型 <code>int[][]</code></li>
* <li>支持带初始值的声明</li>
* </ul>
* 支持以下两种语法结构
* <pre>{@code
* declare myVar:Integer
* declare myVar:Integer = 42 + 3
* }</pre>
* 解析器能够识别多维数组类型 {@code int[]}, {@code string[][]}并支持可选初始化表达式
* <p>
* 每个声明语句均要求以换行符结尾语法不合法时会抛出异常
* </p>
*/
public class DeclarationStatementParser implements StatementParser {
/**
* 解析变量或常量声明语句
* 解析一条 {@code declare} 语句生成对应的抽象语法树节点 {@link DeclarationNode}
* <p>
* 支持类型标注和可选初始化表达式类型部分自动拼接数组维度 int[][]
* </p>
*
* @param ctx 语法分析上下文提供词法单元流与其他辅助功能
* @return 解析得到的声明节点 {@link DeclarationNode}
* @throws org.jcnc.snow.compiler.parser.context.UnexpectedToken 若语法不合法则抛出异常
* @param ctx 当前语法解析上下文包含词法流错误信息等
* @return {@link DeclarationNode} 表示声明语句结构
* @throws RuntimeException 语法不合法时抛出
*/
@Override
public DeclarationNode parse(ParserContext ctx) {
var tokens = ctx.getTokens(); // 获取词法单元流
// 便捷引用词法 token
var tokens = ctx.getTokens();
// 记录声明语句在源码中的位置信息文件名
// 获取当前 token 的行号列号和文件名
int line = tokens.peek().getLine();
int column = tokens.peek().getCol();
String file = ctx.getSourceName();
// 判断并消费声明关键字 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());
}
// 声明语句必须以 "declare" 开头
tokens.expect("declare");
// 获取变量名标识符
String name = tokens.expectType(TokenType.IDENTIFIER).getLexeme();
// 是否声明为常量
boolean isConst = tokens.match("const");
// 检查并消费冒号 :
// 获取变量名称标识符
String name = tokens
.expectType(TokenType.IDENTIFIER)
.getLexeme();
// 类型标注的冒号分隔符
tokens.expect(":");
// 解析变量类型类型标识符或自定义结构体名
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()
// 获取变量类型类型标识符
StringBuilder type = new StringBuilder(
tokens
.expectType(TokenType.TYPE)
.getLexeme()
);
}
// 处理多维数组类型后缀支持 int[][] 等类型
// 消费多维数组类型后缀 "[]"
while (tokens.match("[")) {
// 消费左中括号 '[' 后必须跟右中括号 ']'
tokens.expectType(TokenType.RBRACKET); // 消费 ']'
type.append("[]"); // 追加数组后缀
tokens.expectType(TokenType.RBRACKET); // 必须配对
type.append("[]"); // 类型名称拼接 [] int[][]
}
// 可选的初始化表达式 = 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, // 初始化表达式节点可为 null
new NodeContext(line, column, file) // 源码位置信息
);
// 返回构建好的声明语法树节点
return new DeclarationNode(name, type.toString(), isConst, init, new NodeContext(line, column, file));
}
}

View File

@ -18,13 +18,13 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
* <pre>{@code
* x = 1 + 2 // 赋值语句
* doSomething() // 一般表达式语句
* this.name = n // this.name 赋值语法糖为对 name 的赋值
* }</pre>
*
* - 以标识符开头且后接 '=' 解析为 {@link AssignmentNode}
* - 否则先解析为一般表达式若后续遇到 '='则回退为<expr> = <expr>赋值语句
* - 所有表达式语句必须以换行符NEWLINE结尾
* <p>
* <ul>
* <li>以标识符开头且后接 {@code =} 解析为 {@link AssignmentNode}</li>
* <li>否则视为普通表达式解析为 {@link ExpressionStatementNode}</li>
* <li>所有表达式语句必须以换行符{@code NEWLINE}结尾</li>
* </ul>
* 若语句起始为关键字或空行将直接抛出异常防止非法语法进入表达式解析流程
*/
public class ExpressionStatementParser implements StatementParser {
@ -33,81 +33,49 @@ public class ExpressionStatementParser implements StatementParser {
*
* @param ctx 当前解析上下文提供词法流与环境信息
* @return {@link AssignmentNode} {@link ExpressionStatementNode} 语法节点
* @throws UnexpectedToken 若遇到非法起始关键字 'end'
* @throws UnexpectedToken 若遇到非法起始关键字空行
*/
@Override
public StatementNode parse(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
// ----------- 起始 token 合法性检查放宽以支持 this 开头-----------
if (ts.peek().getType() == TokenType.NEWLINE) {
// 空行不应进入表达式解析直接抛出异常
if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
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 -------------
// 快速路径 "a = ..."直接识别为赋值语句无需完整表达式树回退
// 简单形式: IDENTIFIER = expr
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
String varName = ts.next().getLexeme(); // 消费 IDENTIFIER
ts.expect("="); // 消费 '='
ExpressionNode value = new PrattExpressionParser().parse(ctx); // 解析右侧表达式
String varName = ts.next().getLexeme();
ts.expect("=");
ExpressionNode value = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
// 返回简单变量赋值节点
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
}
// ------------- 通用形式: <expr> [= <expr>] -------------
// 先解析潜在左值表达式或普通表达式
// 尝试解析更通用的左值形式支持下标: <expr> = <expr>
ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
// 若遇到等号则尝试回退为赋值语句兼容更复杂的左值表达式
if ("=".equals(ts.peek().getLexeme())) {
ts.next(); // 消费 '='
ExpressionNode rhs = new PrattExpressionParser().parse(ctx); // 解析右值表达式
ts.next(); // consume '='
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));
}

View File

@ -1,293 +0,0 @@
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}
* <p>
* 解析 <code>struct ... end struct</code> 结构体声明块的顶层语法解析器
* <ul>
* <li>支持解析结构体字段构造函数init结构体方法列表</li>
* <li>按顺序支持 <b>fields/init/function</b> 三种结构体内部块</li>
* <li>语法出错时抛出 {@link UnexpectedToken} 异常便于错误定位</li>
* </ul>
* </p>
*/
public class StructParser implements TopLevelParser {
/**
* 解析结构体声明块并返回 AST 节点 {@link StructNode}
*
* <p>
* 该方法解析结构体声明的头部结构体内部的字段块fields构造函数块init方法块function以及结束标记end struct
* </p>
*
* @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<DeclarationNode> fields = new ArrayList<>();
List<FunctionNode> inits = new ArrayList<>();
List<FunctionNode> 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
* <p>
* 允许包含 params body 两部分顺序不限 "end init" 结束
* </p>
*
* @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<ParameterNode> params = new ArrayList<>();
List<StatementNode> 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 返回参数列表
* <p>
* 且在类型位置同时接受内建类型{@link TokenType#TYPE}与自定义标识符{@link TokenType#IDENTIFIER}
*
* @param ctx 解析上下文
* @return 解析得到的参数节点列表
*/
private List<ParameterNode> parseParams(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
ts.expect("params");
ts.expect(":");
ts.expectType(TokenType.NEWLINE);
List<ParameterNode> 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<StatementNode> parseBody(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
ts.expect("body");
ts.expect(":");
ts.expectType(TokenType.NEWLINE);
List<StatementNode> 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;
}
}

View File

@ -5,10 +5,7 @@ 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.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* {@code ASTJsonSerializer} 是抽象语法树AST序列化工具类
@ -86,8 +83,7 @@ public class ASTJsonSerializer {
return switch (n) {
// 模块节点
case ModuleNode(
String name, List<ImportNode> imports, List<DeclarationNode> globals, List<StructNode> structs,
List<FunctionNode> functions, NodeContext _
String name, List<ImportNode> imports, List<DeclarationNode> globals, List<FunctionNode> functions, NodeContext _
) -> {
Map<String, Object> map = newNodeMap("Module");
map.put("name", name);
@ -109,47 +105,9 @@ public class ASTJsonSerializer {
for (FunctionNode f : functions) {
funcs.add(nodeToMap(f));
}
List<Object> 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<DeclarationNode> fields, List<FunctionNode> inits,
List<FunctionNode> methods, NodeContext _
) -> {
Map<String, Object> map = newNodeMap("Struct");
map.put("name", name);
map.put("parent", parent);
List<Object> lFields = new ArrayList<>();
fields.forEach(d -> lFields.add(Map.of(
"type", "Field",
"name", d.getName(),
"varType", d.getType())));
map.put("fields", lFields);
// 多构造函数序列化为 inits 数组
List<Object> lInits = new ArrayList<>();
if (inits != null) {
for (FunctionNode ctor : inits) {
lInits.add(nodeToMap(ctor));
}
}
map.put("inits", lInits);
List<Object> lMethods = new ArrayList<>();
if (methods != null) {
for (FunctionNode f : methods) lMethods.add(nodeToMap(f));
}
map.put("methods", lMethods);
yield map;
}
// 函数定义节点
case FunctionNode f -> {
Map<String, Object> map = newNodeMap("Function");
@ -231,8 +189,9 @@ public class ASTJsonSerializer {
private static Object exprToMap(ExpressionNode expr) {
return switch (expr) {
// 二元表达式
case BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right, NodeContext _) ->
exprMap("BinaryExpression",
case BinaryExpressionNode(
ExpressionNode left, String operator, ExpressionNode right, NodeContext _
) -> exprMap("BinaryExpression",
"left", exprToMap(left),
"operator", operator,
"right", exprToMap(right)

View File

@ -1,74 +1,44 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
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.*;
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} 是函数调用表达式 ({@link CallExpressionNode}) 的语义分析器
*
* <p>它负责处理所有形式的调用表达式 {@code callee(arg1, arg2, ...)}并执行如下操作
* {@code CallExpressionAnalyzer} 是函数调用表达式的语义分析器
* <p>
* 它负责处理类似 {@code callee(arg1, arg2, ...)} 形式的调用表达式执行如下操作:
* <ul>
* <li>识别调用目标支持三种调用方式
* <ol>
* <li>模块函数调用 {@code module.func(...)} </li>
* <li>结构体实例方法调用 {@code instance.method(...)} </li>
* <li>普通函数调用当前模块或导入模块 {@code func(...)} </li>
* </ol>
* </li>
* <li>在函数解析时遵循如下规则
* <ul>
* <li>若是模块调用必须确认模块已导入</li>
* <li>若是结构体实例调用需先解析左侧表达式类型并确认方法存在</li>
* <li>若是普通函数调用优先在当前模块中查找若未找到则尝试唯一导入模块解析</li>
* <li>识别调用目标支持模块成员函数调用和当前模块函数调用也支持自动在所有已导入模块中查找唯一同名函数</li>
* <li>根据被调用函数的参数签名检查实参数量和类型的兼容性</li>
* <li>支持数值参数的宽化转换 int double</li>
* <li>支持数值到字符串的隐式转换自动视为调用 {@code to_string}</li>
* <li>在发生类型不匹配未导入模块或函数未定义等情况下记录语义错误</li>
* <li>新增"_"开头的函数名只允许在本模块访问禁止跨模块访问</li>
* </ul>
* </li>
* <li>参数检查与类型推断
* <ul>
* <li>检查实参与形参数量是否一致</li>
* <li>检查类型兼容性支持数值宽化转换 (int double)</li>
* <li>支持数值到字符串的隐式转换自动视为调用 {@code to_string}</li>
* </ul>
* </li>
* <li>错误处理
* <ul>
* <li>函数/方法未定义时记录 {@link SemanticError}</li>
* <li>访问未导入的模块时报错</li>
* <li>跨模块访问私有函数 "_" 开头时报错</li>
* <li>参数数量或类型不匹配时报错</li>
* </ul>
* </li>
* <li>最终返回函数的返回类型若分析过程中存在错误返回 {@link BuiltinType#INT} 作为默认回退类型</li>
* </ul>
*
* <p>此分析器是编译器语义分析阶段的重要组成部分确保调用表达式在类型系统和模块作用域中合法</p>
*/
public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> {
/**
* 分析函数调用表达式推断返回类型并执行语义检查
* 分析函数调用表达式并推断其类型
*
* @param ctx 当前语义分析上下文提供日志错误记录模块访问等功能
* @param mi 当前模块信息用于函数查找及模块依赖判断
* @param fn 当前正在分析的函数节点函数作用域
* @param locals 局部符号表用于变量和结构体实例查找
* @param fn 当前分析的函数节点
* @param locals 局部符号表用于变量查找
* @param call 待分析的函数调用表达式节点
* @return 调用表达式的返回类型若存在语义错误返回 {@link BuiltinType#INT}
* @return 表达式的返回类型如果存在语义错误默认返回 {@code BuiltinType.INT}
*/
@Override
public Type analyze(Context ctx,
@ -76,148 +46,88 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
FunctionNode fn,
SymbolTable locals,
CallExpressionNode call) {
ctx.log("检查函数/方法调用: " + call.callee());
ctx.log("检查函数调用: " + call.callee());
ExpressionNode callee = call.callee(); // 被调用的表达式函数名或成员访问
ModuleInfo targetModule = mi; // 初始假设目标模块为当前模块
String functionName; // 被调用函数的名称
FunctionType funcType; // 被调用函数的类型签名
ModuleInfo target = mi; // 默认目标模块为当前模块
String functionName;
ExpressionNode callee = call.callee();
// ========== 支持三种调用形式 ==========
// 1. 模块.函数(...)
// 2. 结构体实例.方法(...)
// 3. 普通函数(...)
if (callee instanceof MemberExpressionNode memberExpr) {
// -------- 情况1 & 情况2: 成员调用表达式 --------
ExpressionNode left = memberExpr.object();
String member = memberExpr.member();
if (left instanceof IdentifierNode idNode) {
// 左侧是标识符可能是模块名或结构体变量
String leftName = idNode.name();
// (1) 模块.函数调用
if (ctx.getModules().containsKey(leftName) &&
(mi.getImports().contains(leftName) || mi.getName().equals(leftName))) {
targetModule = ctx.getModules().get(leftName);
// 支持模块调用形式: ModuleName.FunctionName(...)
if (callee instanceof MemberExpressionNode(var obj, String member, NodeContext _)
&& obj instanceof IdentifierNode(String mod, NodeContext _)) {
// 验证模块是否存在并已导入
if (!ctx.getModules().containsKey(mod)
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
ctx.getErrors().add(new SemanticError(callee,
"未知或未导入模块: " + mod));
ctx.log("错误: 未导入模块 " + mod);
return BuiltinType.INT;
}
target = ctx.getModules().get(mod);
functionName = member;
funcType = targetModule.getFunctions().get(functionName);
} else {
// (2) 结构体实例.方法调用
Symbol sym = locals.resolve(leftName);
if (sym != null && sym.type() instanceof StructType structType) {
funcType = structType.getMethods().get(member);
functionName = member;
if (funcType == null) {
ctx.getErrors().add(new SemanticError(callee,
"结构体方法未定义: " + structType + "." + member));
ctx.log("错误: 结构体方法未定义 " + structType + "." + member);
return BuiltinType.INT;
}
} else {
ctx.getErrors().add(new SemanticError(callee,
"未知或未导入模块: " + leftName));
ctx.log("错误: 未导入模块或未声明变量 " + leftName);
return BuiltinType.INT;
// 简单函数名形式: func(...)
else if (callee instanceof IdentifierNode(String name, NodeContext _)) {
functionName = name;
}
}
} else {
// (2 扩展) 任意表达式.方法调用
Type leftType = ctx.getRegistry()
.getExpressionAnalyzer(left)
.analyze(ctx, mi, fn, locals, left);
if (leftType instanceof StructType structType) {
funcType = structType.getMethods().get(member);
functionName = member;
if (funcType == null) {
ctx.getErrors().add(new SemanticError(callee,
"结构体方法未定义: " + structType + "." + member));
ctx.log("错误: 结构体方法未定义 " + structType + "." + member);
return BuiltinType.INT;
}
} else {
ctx.getErrors().add(new SemanticError(callee,
"不支持的成员调用对象类型: " + leftType));
ctx.log("错误: 不支持的成员调用对象类型 " + leftType);
return BuiltinType.INT;
}
}
} else if (callee instanceof IdentifierNode idNode) {
// -------- 情况3: 普通函数调用 --------
functionName = idNode.name();
funcType = mi.getFunctions().get(functionName); // 优先在当前模块查找
// 若当前模块未定义则尝试在导入模块中唯一解析
if (funcType == null) {
ModuleInfo unique = null;
for (String imp : mi.getImports()) {
ModuleInfo mod = ctx.getModules().get(imp);
if (mod != null && mod.getFunctions().containsKey(functionName)) {
if (unique != null) {
unique = null;
break;
} // 冲突: 不唯一
unique = mod;
}
}
if (unique != null) {
targetModule = unique;
funcType = unique.getFunctions().get(functionName);
}
}
} else {
// 不支持的调用方式 (如直接 lambda/表达式调用)
// 不支持的 callee 形式
else {
ctx.getErrors().add(new SemanticError(callee,
"不支持的调用方式: " + callee));
ctx.log("错误: 不支持的调用方式 " + callee);
return BuiltinType.INT;
}
// -------- 访问控制检查 --------
if (functionName != null && funcType != null &&
functionName.startsWith("_") && !targetModule.getName().equals(mi.getName())) {
// -------------------------
// 私有函数访问控制
// -------------------------
// 如果函数名以"_"开头且不是在本模块调用则报错
if (functionName.startsWith("_") && !target.getName().equals(mi.getName())) {
ctx.getErrors().add(new SemanticError(callee,
"无法访问模块私有函数: " + targetModule.getName() + "." + functionName
"无法访问模块私有函数: " + target.getName() + "." + functionName
+ "(下划线开头的函数只允许在定义模块内访问)"));
ctx.log("错误: 试图跨模块访问私有函数 " + targetModule.getName() + "." + functionName);
ctx.log("错误: 试图跨模块访问私有函数 " + target.getName() + "." + functionName);
return BuiltinType.INT;
}
// -------- 函数是否存在 --------
if (funcType == null) {
// 查找目标函数签名先在当前模块/指定模块查找
FunctionType ft = target.getFunctions().get(functionName);
// 未找到则报错
if (ft == null) {
ctx.getErrors().add(new SemanticError(callee,
"函数未定义: " + functionName));
ctx.log("错误: 函数未定义 " + functionName);
return BuiltinType.INT;
}
// -------- 分析实参类型 --------
// 分析所有实参并获取类型
List<Type> args = new ArrayList<>();
for (ExpressionNode arg : call.arguments()) {
args.add(ctx.getRegistry().getExpressionAnalyzer(arg)
.analyze(ctx, mi, fn, locals, arg));
}
// -------- 参数数量检查 --------
if (args.size() != funcType.paramTypes().size()) {
// 参数数量检查
if (args.size() != ft.paramTypes().size()) {
ctx.getErrors().add(new SemanticError(call,
"参数数量不匹配: 期望 " + funcType.paramTypes().size()
"参数数量不匹配: 期望 " + ft.paramTypes().size()
+ " 个, 实际 " + args.size() + ""));
ctx.log("错误: 参数数量不匹配: 期望 "
+ funcType.paramTypes().size() + ", 实际 " + args.size());
+ ft.paramTypes().size() + ", 实际 " + args.size());
} else {
// -------- 参数类型检查与转换 --------
// 参数类型检查与转换支持
for (int i = 0; i < args.size(); i++) {
Type expected = funcType.paramTypes().get(i);
Type expected = ft.paramTypes().get(i);
Type actual = args.get(i);
// 完全兼容或数值宽化转换
boolean ok = expected.isCompatible(actual)
|| (expected.isNumeric() && actual.isNumeric()
&& Type.widen(actual, expected) == expected);
// 特殊情况数值自动转 string
// 支持将数值自动转换为字符串
if (!ok && expected == BuiltinType.STRING && actual.isNumeric()) {
ctx.log(String.format(
"隐式将参数 %d 的数值类型 %s 转换为 string (to_string)",
@ -226,6 +136,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
ok = true;
}
// 类型不匹配记录语义错误
if (!ok) {
ctx.getErrors().add(new SemanticError(call,
String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
@ -236,8 +147,8 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
}
}
// -------- 返回类型 --------
ctx.log("函数调用类型: 返回 " + funcType.returnType());
return funcType.returnType();
// 返回函数的返回类型作为整个调用表达式的类型
ctx.log("函数调用类型: 返回 " + ft.returnType());
return ft.returnType();
}
}

View File

@ -11,49 +11,39 @@ 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} 用于分析模块成员访问表达式的类型和语义
*
* <p>
* 负责成员访问表达式的语义分析与类型推断
* 典型形如 <code>a.b</code><code>this.x</code><code>Module.member</code>
* 当前实现支持 <code>ModuleName.constOrVar</code> 形式的跨模块常量/全局变量访问
* 能根据目标模块的全局符号表返回准确的类型信息完全支持跨模块类型推断
* <br>
* 对于非模块成员的访问如对象.属性多级 a.b.c暂不支持遇到时将报告语义错误
* </p>
*
* <p>
* 支持三类成员访问方式
* <b>核心特性</b>
* <ul>
* <li>校验模块是否存在是否已导入或自身</li>
* <li>跨模块访问目标模块的全局符号表查找指定成员符号及其类型</li>
* <li>若成员不存在报告模块成员未定义语义错误</li>
* <li>暂不支持更复杂的对象成员访问遇到将报不支持的成员访问对象类型错误</li>
* </ul>
* </p>
* <ol>
* <li><b>当前实例字段语法糖</b> <code>this.x</code>等价于访问当前作用域下名为 <code>x</code> 的变量或字段</li>
* <li><b>跨模块成员访问</b> <code>ModuleName.member</code>常用于引用其他模块的全局变量常量函数等</li>
* <li><b>结构体实例成员/方法访问</b> <code>a.b</code> <code>a.method</code>a 为结构体变量b 为字段或方法</li>
* </ol>
*/
public class MemberExpressionAnalyzer implements ExpressionAnalyzer<MemberExpressionNode> {
/**
* 分析成员表达式并返回其类型如字段类型方法类型等
* 语义分析模块成员访问表达式
*
* @param ctx 全局语义分析上下文
* @param mi 当前模块信息
* @param ctx 全局语义分析上下文持有所有模块及错误记录
* @param mi 当前模块信息用于判断导入关系
* @param fn 当前函数节点
* @param locals 局部符号表当前作用域的变量形参等
* @param expr 当前成员访问表达式形如 obj.member
* @return 该表达式的推断类型如失败则返回 {@link BuiltinType#INT}
*
* <p>
* 主要处理流程分为四大分支
* <ol>
* <li>this.x 语法糖字段或变量访问</li>
* <li>ModuleName.member 跨模块成员访问</li>
* <li>结构体实例的成员/方法访问</li>
* <li>其它对象成员不支持</li>
* </ol>
* 出错时会注册语义错误返回 int 作为降级类型
* </p>
* @param locals 当前局部符号表
* @param expr 当前要分析的成员表达式 ModuleA.a
* @return 成员表达式的类型出错时类型降级为 int并记录语义错误
*/
@Override
public Type analyze(Context ctx,
@ -64,117 +54,41 @@ public class MemberExpressionAnalyzer implements ExpressionAnalyzer<MemberExpres
ctx.log("检查成员访问: " + expr);
// =====================================================================
// 1) 处理 this.x 语法糖
// =====================================================================
// 匹配左侧为 IdentifierNode且为 "this"
if (expr.object() instanceof IdentifierNode oid) {
String objName = oid.name();
// -------- 仅支持 ModuleName.member 形式 --------
if (expr.object() instanceof IdentifierNode(String mod, NodeContext _)) {
if ("this".equals(objName)) {
// 优先在当前局部符号表查找字段或变量如结构体字段/参数/局部变量等
if (locals != null) {
Symbol localSym = locals.resolve(expr.member());
if (localSym != null) {
return localSym.type();
}
}
// 再查全局符号表当前模块全局声明
SymbolTable globals = mi != null ? mi.getGlobals() : null;
if (globals != null) {
Symbol globalSym = globals.resolve(expr.member());
if (globalSym != null) {
return globalSym.type();
}
}
// 两处都查不到则报错
// 1. 校验模块存在且已导入或为本模块自身
if (!ctx.getModules().containsKey(mod)
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
ctx.getErrors().add(new SemanticError(expr,
"未定义的字段或变量: " + expr.member()));
ctx.log("错误: 未定义的字段或变量 this." + expr.member());
// 降级为 int 类型
"未知或未导入模块: " + mod));
ctx.log("错误: 未导入模块 " + mod);
return BuiltinType.INT;
}
}
// =====================================================================
// 2) 处理 ModuleName.member 跨模块访问
// =====================================================================
// 左侧为 IdentifierNode尝试解析为模块名
if (expr.object() instanceof IdentifierNode(String mod, NodeContext _)) {
// 是否为已知模块
boolean moduleExists = ctx.getModules().containsKey(mod);
// 是否导入了该模块或就是本模块允许自引用
boolean importedOrSelf = mi != null && (mi.getName().equals(mod) || mi.getImports().contains(mod));
if (moduleExists && importedOrSelf) {
// 2. 查找目标模块的全局符号表精确返回成员类型
ModuleInfo target = ctx.getModules().get(mod);
// 1) 先查模块内函数支持模块.函数名引用
FunctionType ft = target.getFunctions().get(expr.member());
if (ft != null) return ft;
// 2) 再查模块全局符号表全局常量/变量
SymbolTable globals = target != null ? target.getGlobals() : null;
SymbolTable globals = target.getGlobals();
if (globals != null) {
Symbol sym = globals.resolve(expr.member());
if (sym != null) {
return sym.type();
return sym.type(); // 找到成员返回其真实类型
}
}
// 以上均未找到则报错
// 3. 成员不存在记录语义错误并类型降级
ctx.getErrors().add(new SemanticError(expr,
"模块成员未定义: " + mod + "." + expr.member()));
ctx.log("错误: 模块成员未定义 " + mod + "." + expr.member());
return BuiltinType.INT;
}
// 左侧不是模块名可能是变量名结构体实例进入结构体成员分支
if (locals != null) {
Symbol sym = locals.resolve(mod);
if (sym != null && sym.type() instanceof StructType st) {
// 结构体字段访问
Type t = st.getFields().get(expr.member());
if (t != null) return t;
// 结构体方法访问返回方法类型用于引用/分派
FunctionType ft = st.getMethods().get(expr.member());
if (ft != null) return ft;
// 字段和方法都没有报错
ctx.getErrors().add(new SemanticError(expr,
"结构体成员未定义: " + st + "." + expr.member()));
ctx.log("错误: 结构体成员未定义 " + st + "." + expr.member());
return BuiltinType.INT;
}
}
// 非模块非变量均未找到报错
ctx.getErrors().add(new SemanticError(expr, "未知或未导入模块: " + mod));
ctx.log("错误: 未导入模块或未声明变量 " + mod);
return BuiltinType.INT;
}
// =====================================================================
// 3) 支持通用结构体成员访问a.b 或任意表达式.b
// =====================================================================
// 动态推断左侧表达式类型如结构体类型等
Type leftType = ctx.getRegistry().getExpressionAnalyzer(expr.object())
.analyze(ctx, mi, fn, locals, expr.object());
if (leftType instanceof StructType st) {
// 字段访问
Type t = st.getFields().get(expr.member());
if (t != null) return t;
// 方法访问返回方法类型支持引用/分派
FunctionType ft = st.getMethods().get(expr.member());
if (ft != null) return ft;
// 均未找到报错
// -------- 其它对象成员 a.b.c暂不支持 --------
ctx.getErrors().add(new SemanticError(expr,
"结构体成员未定义: " + st + "." + expr.member()));
ctx.log("错误: 结构体成员未定义 " + st + "." + expr.member());
return BuiltinType.INT;
}
// =====================================================================
// 4) 其它对象成员 xx.yy xx 非结构体暂不支持
// =====================================================================
ctx.getErrors().add(new SemanticError(expr,
"不支持的成员访问对象类型: " + expr.object().getClass().getSimpleName()));
ctx.log("错误: 不支持的成员访问对象类型 " + expr.object().getClass().getSimpleName());
"不支持的成员访问对象类型: "
+ expr.object().getClass().getSimpleName()));
ctx.log("错误: 不支持的成员访问对象类型 "
+ expr.object().getClass().getSimpleName());
return BuiltinType.INT;
}
}

View File

@ -1,116 +0,0 @@
package org.jcnc.snow.compiler.semantic.analyzers.expression;
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
import org.jcnc.snow.compiler.parser.ast.NewExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
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.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 NewExpressionAnalyzer}
* <p>
* 负责对象创建表达式<code>new T(args...)</code>的语义分析与类型推断
* <ul>
* <li>分析 new 关键字创建结构体对象的表达式</li>
* <li>校验目标类型是否为结构体参数数量与类型是否匹配构造函数</li>
* <li>出错时会收集详细的语义错误信息</li>
* </ul>
*
* <p>
* <b>表达式类型</b> 返回被 new 的结构体类型 <code>T</code>如失败则降级为 int
* </p>
*/
public class NewExpressionAnalyzer implements ExpressionAnalyzer<NewExpressionNode> {
/**
* 语义分析推断 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<Type> 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<Type> 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;
}
}

View File

@ -56,11 +56,17 @@ 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(IndexExpressionNode.class,new IndexExpressionAnalyzer()); // 关键行
registry.registerStatementAnalyzer(IndexAssignmentNode.class, new IndexAssignmentAnalyzer());
// ---------- 注册一元表达式分析器 ----------
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
registry.registerExpressionAnalyzer(NewExpressionNode.class, new NewExpressionAnalyzer());
registry.registerExpressionAnalyzer(MemberExpressionNode.class, new MemberExpressionAnalyzer());
// ---------- 成员访问表达式 ----------
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
new MemberExpressionAnalyzer());
}
}

View File

@ -9,61 +9,47 @@ import java.util.List;
import java.util.Map;
/**
* {@code Context} 表示语义分析阶段的全局上下文环境贯穿于编译器语义分析流程中
* {@code Context} 表示语义分析阶段的全局上下文环境
* <p>
* 主要负责
* 该类贯穿整个语义分析流程集中管理以下内容
* <ul>
* <li>模块信息的统一管理含模块结构体类型</li>
* <li>收集和存储语义分析阶段产生的所有错误</li>
* <li>根据配置输出调试日志</li>
* <li>调度各类语义分析器执行</li>
* <li>类型解析包括内建类型数组类型结构体等自定义类型</li>
* <li>模块信息管理所有已加载模块包括源模块和内置模块</li>
* <li>错误收集集中存储语义分析期间产生的 {@link SemanticError}</li>
* <li>日志输出控制可选支持调试信息</li>
* <li>分析器调度通过 {@link AnalyzerRegistry} 分发对应分析器</li>
* </ul>
* 该类为所有分析器提供便捷的访问查找和工具方法
* <p>
* 提供便捷的 getter 方法和类型解析工具方法
* </p>
*/
public class Context {
/**
* 所有模块信息表
* <p>
* 键为模块名值为 {@link ModuleInfo} 实例用于支持跨模块类型/结构体引用
* 模块表模块名 {@link ModuleInfo}用于模块查找与跨模块引用
*/
private final Map<String, ModuleInfo> modules;
/**
* 语义分析期间收集的所有错误对象
* <p>
* 每当发现语义错误时 {@link SemanticError} 添加到该列表
* 错误列表语义分析过程中收集的所有 {@link SemanticError}
*/
private final List<SemanticError> errors;
/**
* 日志输出开关
* <p>
* true 调用 {@link #log(String)} 会输出日志信息用于调试
* 日志开关若为 true将启用 {@link #log(String)} 输出日志信息
*/
private final boolean verbose;
/**
* 语义分析器注册表
* <p>
* 用于根据语法树节点类型动态分派对应的分析器
* 语义分析器注册表用于按节点类型动态调度分析器
*/
private final AnalyzerRegistry registry;
/**
* 当前正在处理的模块名
* <p>
* 主要用于类型解析时限定结构体查找的作用域
*/
private String currentModuleName;
/**
* 构造一个新的语义分析上下文实例
* 构造语义分析上下文对象
*
* @param modules 所有已加载模块的映射表
* @param errors 用于收集错误的列表
* @param verbose 是否输出日志
* @param registry 分析器注册表
* @param modules 已注册的模块信息集合
* @param errors 错误收集器分析器将所有语义错误写入此列表
* @param verbose 是否启用调试日志输出
* @param registry 分析器注册表提供类型到分析器的映射与调度能力
*/
public Context(Map<String, ModuleInfo> modules,
List<SemanticError> errors,
@ -75,137 +61,102 @@ public class Context {
this.registry = registry;
}
// ==== Getter 方法基本访问器 ====
// ------------------ 模块信息 ------------------
/**
* 获取所有模块信息表模块名 ModuleInfo
*/
public Map<String, ModuleInfo> modules() {
return modules;
}
/**
* 获取所有模块信息表 {@link #modules()} 等价
* 获取所有模块信息映射表
*
* @return 模块名到模块信息{@link ModuleInfo}的映射表
*/
public Map<String, ModuleInfo> getModules() {
return modules;
}
/**
* 获取语义分析期间收集的所有错误
* 模块信息 getter快捷方式
*
* @return 模块名到模块信息{@link ModuleInfo}的映射表
*/
public List<SemanticError> errors() {
return errors;
public Map<String, ModuleInfo> modules() {
return modules;
}
// ------------------ 错误收集 ------------------
/**
* 获取语义分析期间收集的所有错误 {@link #errors()} 等价
* 获取语义分析过程中记录的所有错误
*
* @return 错误列表
*/
public List<SemanticError> getErrors() {
return errors;
}
/**
* 获取语义分析器注册表
* 错误列表 getter快捷方式
*
* @return 错误列表
*/
public List<SemanticError> errors() {
return errors;
}
// ------------------ 分析器注册表 ------------------
/**
* 获取分析器注册表用于分发语句与表达式分析器
*
* @return {@link AnalyzerRegistry} 实例
*/
public AnalyzerRegistry getRegistry() {
return registry;
}
/**
* 输出语义分析日志信息 verbose 控制
* 注册表 getter快捷方式
*
* @param msg 日志消息内容
* @return {@link AnalyzerRegistry} 实例
*/
public AnalyzerRegistry registry() {
return registry;
}
// ------------------ 日志输出 ------------------
/**
* 打印日志信息仅当 {@code verbose} true 时生效
*
* @param msg 日志内容
*/
public void log(String msg) {
if (verbose) System.out.println("[semantic] " + msg);
if (verbose) {
System.out.println("[SemanticAnalyzer] " + msg);
}
}
// ==== 当前模块管理 ====
// ------------------ 工具函数 ------------------
/**
* 获取当前正在分析的模块名
*
* @return 当前模块名可能为 null
*/
public String getCurrentModule() {
return currentModuleName;
}
/**
* 设置当前正在分析的模块名
* 将类型名称字符串解析为对应的类型实例支持多维数组后缀
* <p>
* 用于限定结构体查找的优先作用域
* 例如"int" int 类型"int[][]" 二维整型数组类型
* </p>
*
* @param moduleName 当前模块名
* @param name 类型名称支持 "[]" 数组后缀
* @return 对应的 {@link Type} 实例若无法识别返回 null
*/
public void setCurrentModule(String moduleName) {
this.currentModuleName = moduleName;
}
// ==== 类型解析工具 ====
/**
* 解析类型字符串为 {@link Type} 实例
* <p>
* 支持内建类型数组类型 "[]" 后缀用户自定义结构体类型
* 类型解析的查找顺序为<br>
* 1. 内建类型<br>
* 2. 当前模块定义的结构体类型<br>
* 3. 当前模块导入模块中的结构体类型<br>
* 4. 全局所有模块的结构体类型
*
* @param typeName 类型名称字符串 "int""Foo""Bar[][]"
* @return 解析出的 {@link Type} 实例若找不到则返回 null
*/
public Type parseType(String typeName) {
if (typeName == null) return null; // 处理空输入
String name = typeName; // 剥离数组后缀前的基本类型名
int dims = 0; // 记录数组维度
// 支持多维数组 "int[][]" -> dims=2
public Type parseType(String name) {
int dims = 0;
while (name.endsWith("[]")) {
name = name.substring(0, name.length() - 2);
dims++;
}
// 1) 优先查找内建类型
Type base = BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
// 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;
}
}
}
}
// 2.3 全局所有模块的结构体作为兜底
if (base == null) {
for (ModuleInfo im : modules.values()) {
if (im.getStructs().containsKey(name)) {
base = im.getStructs().get(name);
break;
}
}
}
}
if (base == null) return null; // 所有路径均未找到
// 包装数组类型根据数组维度递归封装
if (base == null) return null;
Type t = base;
for (int i = 0; i < dims; i++) t = new ArrayType(t);
for (int i = 0; i < dims; i++) {
t = new ArrayType(t);
}
return t;
}
}

View File

@ -4,110 +4,123 @@ 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} 负责对所有模块的函数体进行两遍扫描式的语义检查
* {@code FunctionChecker} Snow 编译器语义分析阶段用于检查所有函数体合法性的总控调度器
* <p>
* 检查流程为
* <ol>
* <li>第一遍为每个模块构建全局符号表注册所有模块级变量与常量声明并检查重复/未知类型</li>
* <li>第二遍在所有全局符号表准备好后依次分析各模块下所有函数的参数和函数体语句</li>
* </ol>
* 检查要点
* <b>设计核心</b>采用两遍扫描方案彻底解决跨模块全局变量/常量类型推断和引用依赖问题
* <ul>
* <li>类型未知或变量/常量重复声明时均收集为语义错误</li>
* <li>所有函数参数注册为局部变量</li>
* <li>所有函数体语句分派到对应 StatementAnalyzer 实例做分析</li>
* <li> void 返回类型的函数必须有至少一条 return</li>
* <li><b>第一遍</b>为所有模块预先构建并注册其全局符号表globals保证跨模块引用时可见</li>
* <li><b>第二遍</b>在全局符号表全部就绪后依次分析所有模块的函数体实现局部作用域类型推断语义校验等任务</li>
* </ul>
* <b>功能职责</b>
* <ul>
* <li>遍历所有模块先建立 globals再遍历并检查所有函数体语句</li>
* <li>为每个函数体构建完整符号表并注册参数变量</li>
* <li>分发每条语句到对应 {@link StatementAnalyzer} 进行类型检查和错误校验</li>
* <li>自动检查非 void 函数 return 完备性</li>
* <li>记录所有语义错误便于前端高亮和诊断</li>
* </ul>
*
* @param ctx 全局语义分析上下文持有模块信息符号表错误收集等资源
*/
public record FunctionChecker(Context ctx) {
/**
* 对传入的所有模块做函数体的两遍扫描式语义检查
* 主入口对所有模块的所有函数体进行语义检查两遍扫描实现
* <p>
* <b>第一遍</b>为每个模块提前构建全局符号表包含本模块所有全局变量和常量
* 并注册到 {@link ModuleInfo}确保跨模块引用时所有全局符号都已可用
* <br>
* <b>第二遍</b>遍历所有模块的所有函数对每个函数体
* <ul>
* <li>构建局部作用域父作用域为对应模块的 globals</li>
* <li>注册参数变量</li>
* <li>依次分发每条语句到对应 {@link StatementAnalyzer}进行类型和语义检查</li>
* <li>自动校验非 void 函数 return 完备性</li>
* <li>将所有发现的问题统一记录到 {@link SemanticError} 列表</li>
* </ul>
*
* @param mods 所有待分析的模块 AST 节点集合
* @param mods 所有模块 AST 节点集合
*/
public void check(Iterable<ModuleNode> mods) {
List<ModuleNode> 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()) {
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;
var t = ctx.parseType(g.getType());
SymbolKind k = g.isConst() ? SymbolKind.CONSTANT : SymbolKind.VARIABLE;
String dupType = g.isConst() ? "常量" : "变量";
// 注册符号表防止重名
if (!globalScope.define(new Symbol(g.getName(), t, kind))) {
ctx.errors().add(new SemanticError(g, dupType + "重复声明: " + g.getName()));
// 检查重复声明
if (!globalScope.define(new Symbol(g.getName(), t, k))) {
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);
// 注册所有函数参数到局部作用域类型未知时兜底为 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));
});
// 注册函数参数为局部变量
fn.parameters().forEach(p ->
locals.define(new Symbol(
p.name(),
ctx.parseType(p.type()),
SymbolKind.VARIABLE
))
);
// 分析函数体所有语句
for (StatementNode stmt : fn.body()) {
StatementAnalyzer<StatementNode> analyzer =
ctx.getRegistry().getStatementAnalyzer(stmt);
// 分析函数体内每条语句
for (var stmt : fn.body()) {
var 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 语句
Type ret = ctx.parseType(fn.returnType());
if (ret != null && ret != BuiltinType.VOID) {
boolean hasReturn = fn.body().stream().anyMatch(s -> s instanceof ReturnNode);
// 检查非 void 函数是否至少包含一条 return 语句
var returnType = ctx.parseType(fn.returnType());
if (returnType != BuiltinType.VOID) {
boolean hasReturn = fn.body().stream()
.anyMatch(stmtNode -> stmtNode instanceof ReturnNode);
if (!hasReturn) {
ctx.errors().add(new SemanticError(fn, "非 void 函数必须包含至少一条 return 语句"));
ctx.errors().add(new SemanticError(
fn,
"非 void 函数必须包含至少一条 return 语句"
));
}
}
}

View File

@ -2,7 +2,6 @@ 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;
@ -10,155 +9,106 @@ import java.util.Map;
import java.util.Set;
/**
* {@code ModuleInfo} 表示单个模块在语义分析阶段的所有元信息封装
*
* {@code ModuleInfo} 表示单个模块在语义分析阶段的元信息封装
* <p>
* 每个模块对应一个唯一的 ModuleInfo 实例在语义分析期间用于管理和查询
* 用于在分析期间管理模块间依赖函数签名查找全局符号表等关键任务
* 每个模块对应一个唯一的 {@code ModuleInfo} 实例贯穿整个语义分析流程
*
* <p><b>包含信息</b>
* <ul>
* <li>模块名称全局唯一标识</li>
* <li>本模块导入的其他模块名集合跨模块依赖支持</li>
* <li>该模块中所有函数签名用于查找/重名检测/类型推断</li>
* <li>模块级全局符号表常量全局变量支持类型推断和常量折叠</li>
* <li>本模块定义的结构体类型struct</li>
* <li>该模块导入的其他模块名集合跨模块引用支持</li>
* <li>该模块中定义的所有函数签名 {@code Map<String, FunctionType>}</li>
* <li>模块级全局符号表 {@link SymbolTable}常量 / 全局变量支持跨模块类型推断</li>
* </ul>
* </p>
*
* <p><b>主要用途</b>
* <p><b>典型用途</b>
* <ul>
* <li>跨模块语义分析符号查找类型推断常量查找函数/类型重名校验等</li>
* <li>所有对该模块的引用类型检查全局变量访问等均依赖本类提供的索引信息</li>
* <li>用于函数签名类型查找重名检测跨模块引用校验等</li>
* <li>全局符号表为类型检查与后端 IR 常量折叠等模块级分析提供支撑</li>
* </ul>
* </p>
*/
public class ModuleInfo {
/**
* 模块名称作为全局唯一标识符
* 不可为空通常为文件名或逻辑模块名
* 模块名称作为全局唯一标识
*/
private final String name;
/**
* 该模块显式导入的模块名集合
* <p>
* 仅存储本模块 import 的模块名 import foo;
* 用于限制跨模块访问符号时的合法性校验
* </p>
* <p>
* 注意集合为内部引用允许外部直接添加或删除导入关系
* </p>
* 该模块显式导入的模块名集合用于跨模块访问符号
*/
private final Set<String> imports = new HashSet<>();
/**
* 该模块中声明的所有函数签名映射
* <p>
* 键为函数名值为函数类型
* 支持跨模块函数引用重名检测和类型推断
* </p>
* 该模块中定义的函数名 函数类型映射
*/
private final Map<String, FunctionType> functions = new HashMap<>();
/**
* 该模块中声明的所有结构体类型struct
* <p>
* 键为结构体名称值为结构体类型
* 用于跨模块/跨作用域结构体查找与类型检查
* </p>
*/
private final Map<String, StructType> structs = new HashMap<>();
/**
* 模块级全局符号表
* <p>
* 记录本模块声明的所有常量和全局变量
* 支持类型推断跨模块常量折叠全局符号查找
* FunctionChecker 阶段注入
* </p>
* 模块级全局符号表常量 / 全局变量
*/
private SymbolTable globals;
/**
* 构造函数根据模块名初始化模块元信息
* 构造模块信息对象
*
* @param name 模块名称要求全局唯一且不可为空
* @param name 模块名称必须唯一且不可为空
*/
public ModuleInfo(String name) {
this.name = name;
}
/**
* 获取当前模块的全局唯一名称
* 获取模块名称
*
* @return 模块名字符串
* @return 当前模块的唯一名称
*/
public String getName() {
return name;
}
/**
* 获取本模块导入的所有模块名称集合
*
* 获取该模块导入的模块名称集合
* <p>
* 注意返回为内部集合引用调用方可直接对集合进行 add/remove 操作维护导入依赖关系
* </p>
* 返回集合为内部数据的直接引用调用方可通过 {@code add}/{@code remove} 方法动态维护导入信息
*
* @return 可变集合包含所有导入模块名 "foo", "bar"
* @return 可变集合包含所有导入模块名
*/
public Set<String> getImports() {
return imports;
}
/**
* 获取模块内所有函数签名表
*
* 获取模块中已声明的函数签名表
* <p>
* 键为函数名值为函数类型
* 返回对象为内部映射可用于动态添加/修改/删除函数定义
* </p>
* 映射键为函数名值为对应的 {@link FunctionType}
* 返回对象为内部引用可用于添加修改或删除函数定义
*
* @return 函数名 函数类型映射表
* @return 模块内函数定义映射表
*/
public Map<String, FunctionType> getFunctions() {
return functions;
}
/**
* 获取模块定义的所有结构体类型
*
* 获取模块的全局符号表包含常量与全局变量
* <p>
* 键为结构体名值为结构体类型描述
* </p>
* 该符号表由语义分析的 FunctionChecker 阶段构建完成并注入
* 提供跨模块类型检查常量折叠等能力
*
* @return 结构体名 结构体类型映射
*/
public Map<String, StructType> getStructs() {
return structs;
}
/**
* 获取当前模块的全局符号表包含常量与全局变量
*
* <p>
* 该符号表通常由 FunctionChecker 阶段在全局扫描时构建并注入
* 提供后续类型检查常量折叠跨模块全局符号访问等能力
* </p>
*
* @return 本模块全局符号表
* @return 当前模块的全局符号表
*/
public SymbolTable getGlobals() {
return globals;
}
/**
* 设置模块的全局符号表仅限 FunctionChecker 构建时注入
*
* 设置模块的全局符号表
* <p>
* 通常仅语义分析的全局阶段由框架内部调用
* 不建议外部用户主动修改此符号表
* </p>
* 仅应由 FunctionChecker 在语义分析全局扫描阶段调用
*
* @param globals 新的全局符号表实例
* @param globals 全局符号表实例
*/
public void setGlobals(SymbolTable globals) {
this.globals = globals;

View File

@ -1,10 +1,12 @@
package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.parser.ast.*;
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.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;
@ -12,118 +14,71 @@ import java.util.List;
import java.util.Optional;
/**
* {@code SignatureRegistrar}
* {@code SignatureRegistrar} 负责函数签名登记与导入语义检查
* <p>
* 语义分析准备阶段负责函数签名登记结构体签名登记与 import 校验
*
* 在语义分析初期阶段它遍历每个模块完成以下任务:
* <ul>
* <li>验证每个模块声明的 import 模块在全局模块表 {@link Context#modules()} 是否存在</li>
* <li>将每个函数结构体方法构造函数的类型签名登记到 {@link ModuleInfo}便于后续类型推断</li>
* <li>支持 {@code extends} 单继承子类会继承父类的字段与方法</li>
* <li>若参数或返回类型无法解析则报错并降级为 int void保证语义分析流程健壮</li>
* <li>验证所有 {@link ImportNode} 导入的模块是否存在于全局模块表 {@link Context#modules()} </li>
* <li>将每个 {@link FunctionNode} 的函数签名参数类型和返回类型注册到对应 {@link ModuleInfo} </li>
* <li>在参数或返回类型无法识别时记录 {@link SemanticError}并进行容错降级</li>
* </ul>
* <p>
* 作为语义分析前置流程为后续函数体和表达式分析提供类型环境
* 本组件作为语义分析的准备阶段为后续函数体检查提供函数类型上下文
*
* @param ctx 全局语义分析上下文提供模块类型错误管理等功能
*/
public record SignatureRegistrar(Context ctx) {
/**
* 遍历所有模块注册函数/方法/结构体签名校验 import 合法性
* 构造函数签名注册器
*
* @param modules 需要分析的所有模块列表AST 顶层节点
* @param ctx 当前语义分析上下文
*/
public void register(Iterable<ModuleNode> modules) {
for (ModuleNode mod : modules) {
ctx.setCurrentModule(mod.name()); // 切换上下文到当前模块
public SignatureRegistrar {
}
/**
* 遍历模块并注册函数签名同时校验导入模块的合法性
*
* @param mods 所有模块的语法树节点集合
*/
public void register(Iterable<ModuleNode> mods) {
for (ModuleNode mod : mods) {
ModuleInfo mi = ctx.modules().get(mod.name());
// ========== 1) 校验 imports ==========
// ---------- 1. 模块导入检查 ----------
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) 结构体签名登记 ==========
for (StructNode stn : mod.structs()) {
// 构造结构体类型对象唯一标识为 (模块名, 结构体名)
StructType st = new StructType(mod.name(), stn.name());
mi.getStructs().put(stn.name(), st);
// --- 2.1 多个构造函数 init重载按参数个数区分 ---
if (stn.inits() != null) {
for (FunctionNode initFn : stn.inits()) {
List<Type> 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<Type> 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 = mi.getStructs().get(stn.parent());
if (parent == null) {
// 父类不存在报语义错误
ctx.errors().add(new SemanticError(stn,
"未知父类: " + stn.parent()));
} else {
// 将父类字段拷贝到子类若子类未定义同名字段
parent.getFields().forEach(
(k, v) -> child.getFields().putIfAbsent(k, v));
// 将父类方法拷贝到子类若子类未覆盖
parent.getMethods().forEach(
(k, v) -> child.getMethods().putIfAbsent(k, v));
// 构造函数不继承由子类自行声明
}
}
}
// ========== 3) 模块级函数签名登记 ==========
// ---------- 2. 函数签名注册 ----------
for (FunctionNode fn : mod.functions()) {
List<Type> params = 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;
}
Type t = Optional.ofNullable(ctx.parseType(p.type()))
.orElseGet(() -> {
ctx.errors().add(new SemanticError(
p,
"未知类型: " + p.type()
));
return 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));
}
}

View File

@ -1,186 +0,0 @@
package org.jcnc.snow.compiler.semantic.type;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* {@code StructType}
* <p>
* 表示用户自定义结构体类型在类型系统中用于描述结构体实例的静态类型信息
* <ul>
* <li> <b>(moduleName, structName)</b> 唯一标识支持跨模块同名 struct</li>
* <li>包含字段定义方法签名构造函数等全部类型元信息</li>
* </ul>
* </p>
*/
public class StructType implements Type {
/**
* 所属模块名称用于唯一标识支持跨模块同名结构体
*/
private final String moduleName;
/**
* 结构体类型名称 "Person"
*/
private final String name;
/**
* 形参个数 构造函数签名
*/
private final Map<Integer, FunctionType> constructors = new HashMap<>();
/**
* 字段定义表字段名 -> 字段类型
*/
private final Map<String, Type> fields = new HashMap<>();
/**
* 方法签名表方法名 -> 函数类型
*/
private final Map<String, FunctionType> methods = new HashMap<>();
/**
* 构造函数init的函数类型可为 null表示无参默认构造
*/
private FunctionType constructor;
/**
* 构造函数创建结构体类型描述
*
* @param moduleName 所属模块名不可为空
* @param name 结构体名称不可为空
*/
public StructType(String moduleName, String name) {
this.moduleName = moduleName;
this.name = name;
}
/**
* 获取结构体所属模块名
*
* @return 模块名称
*/
public String moduleName() {
return moduleName;
}
/**
* 添加构造函数签名
*
* @param ctor 构造函数的 {@link FunctionType} 实例
*/
public void addConstructor(FunctionType ctor) {
constructors.put(ctor.paramTypes().size(), ctor);
}
/**
* 根据参数个数获取对应构造函数
*
* @param argc 参数个数
* @return 对应的构造函数签名找不到则返回 null
*/
public FunctionType getConstructor(int argc) {
return constructors.get(argc);
}
/**
* 获取所有构造函数签名不可变视图
*
* @return 形参个数 构造函数签名的映射
*/
public Map<Integer, FunctionType> getConstructors() {
return Map.copyOf(constructors);
}
/**
* 获取结构体名称
*
* @return 结构体类型名
*/
@Override
public String name() {
return name;
}
/**
* 获取默认构造函数签名
*
* @return 默认构造函数可能为 null表示无参构造
*/
public FunctionType getConstructor() {
return constructor;
}
/**
* 设置默认构造函数签名
*
* @param ctor 构造函数类型
*/
public void setConstructor(FunctionType ctor) {
this.constructor = ctor;
}
/**
* 获取所有字段定义字段名 字段类型
*
* @return 字段定义表
*/
public Map<String, Type> getFields() {
return fields;
}
/**
* 获取所有方法签名方法名 函数类型
*
* @return 方法签名表
*/
public Map<String, FunctionType> getMethods() {
return methods;
}
/**
* 判断类型兼容性
* <ul>
* <li>仅模块名与结构体名都相等才视为兼容</li>
* <li>跨模块同名 struct 不兼容</li>
* </ul>
*
* @param other 另一个类型
* @return 类型兼容返回 true否则 false
*/
@Override
public boolean isCompatible(Type other) {
if (!(other instanceof StructType s)) return false;
return Objects.equals(this.name, s.name)
&& Objects.equals(this.moduleName, s.moduleName);
}
/**
* 判断类型相等模块名+结构体名全等
*
* @param o 另一个对象
* @return 相等返回 true否则 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);
}
/**
* 哈希值定义 equals 保持一致用于 Map/Set 索引
*
* @return 哈希码
*/
@Override
public int hashCode() {
return Objects.hash(moduleName, name);
}
/**
* 字符串表示返回结构体名调试用
*
* @return 结构体名字符串
*/
@Override
public String toString() {
return name;
}
}

View File

@ -31,61 +31,71 @@ import java.util.*;
import static org.jcnc.snow.common.SnowConfig.print;
/**
* CompileTask: .snow 源文件编译为 VM 字节码文件 (.water) CLI 任务实现
* CLI 任务: 编译 .snow 源文件为 VM 字节码.water 文件
* <p>
* 用法示例
* 支持单文件多文件和目录递归编译并可在编译后立即运行虚拟机<br>
* 命令行参数支持 run-o-d 及直接指定源文件
* </p>
*
* <pre>
* snow compile [run] [-o &lt;name&gt;] [-d &lt;srcDir&gt;] [file1.snow file2.snow ...]
* 用法示例:
* $ snow compile [run] [-o &lt;name&gt;] [-d &lt;srcDir&gt;] [file1.snow file2.snow ...]
* </pre>
* 支持编译完成后立即运行生成的 VM
*/
public record CompileTask(Project project, String[] args) implements Task {
/* ------------------------------------------------------------------ */
/* 1. 构造方法和成员变量 */
/* ------------------------------------------------------------------ */
public final class CompileTask implements Task {
/**
* 项目信息
*/
private final Project project;
/**
* 原始命令行参数
*/
private final String[] args;
/**
* 使用默认空参数数组的构造方法
* 创建一个编译任务
*
* @param project 当前项目对象
* @param project 项目信息对象
* @param args 命令行参数数组
*/
public CompileTask(Project project, String[] args) {
this.project = project;
this.args = args;
}
/**
* 创建一个不带参数的编译任务
*
* @param project 项目信息对象
*/
public CompileTask(Project project) {
this(project, new String[0]);
}
/* ------------------------------------------------------------------ */
/* 2. 工具方法 */
/* ------------------------------------------------------------------ */
/* ---------------------- 调试输出工具 ---------------------- */
/**
* 推断输出 .water 文件的路径
* <p>
* 规则
* 推断 .water 输出文件名
* <ul>
* <li>若指定了 outName则以 outName 为基名</li>
* <li>否则若指定了目录则以目录名为基名</li>
* <li>否则若只有一个源文件则以该文件名 .snow 后缀为基名</li>
* <li>否则默认 "program"</li>
* <li>如果指定 -o直接使用该名称</li>
* <li>目录编译时取目录名</li>
* <li>单文件编译时取文件名去掉 .snow 后缀</li>
* <li>否则默认 "program"</li>
* </ul>
*
* @param sources 源文件列表
* @param outName 用户指定的输出文件
* @param dir 用户指定的源目录
* @return 推断得到的 .water 文件路径
* @param sources 源文件路径列表
* @param outName 输出文件名如有指定否则为 null
* @param dir 目录如有指定否则为 null
* @return 推断出的输出文件路径.water 文件
*/
private static Path deriveOutputPath(List<Path> sources,
String outName,
Path dir) {
private static Path deriveOutputPath(List<Path> 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";
@ -94,16 +104,15 @@ public record CompileTask(Project project, String[] args) implements Task {
}
/**
* main *.main 函数调整到列表首位确保程序入口的 PC = 0
* main 函数调整至函数列表首位确保程序入口为 PC=0
*
* @param in 原始 IRProgram
* @return 调整入口顺序后的 IRProgram
* @return 调整入口后的 IRProgram
*/
private static IRProgram reorderForEntry(IRProgram in) {
List<IRFunction> ordered = new ArrayList<>(in.functions());
for (int i = 0; i < ordered.size(); i++) {
String fn = ordered.get(i).name();
if ("main".equals(fn) || fn.endsWith(".main")) {
if ("main".equals(ordered.get(i).name())) {
Collections.swap(ordered, 0, i);
break;
}
@ -113,14 +122,10 @@ public record CompileTask(Project project, String[] args) implements Task {
return out;
}
/* ------------------------------------------------------------------ */
/* 3. 任务执行入口 */
/* ------------------------------------------------------------------ */
/**
* 执行任务入口调用 execute 并处理异常
* 执行编译任务该方法会解析参数并调用 {@link #execute(String[])} 进行实际编译流程
*
* @throws Exception 执行过程出现异常
* @throws Exception 执行过程中出现任意异常时抛出
*/
@Override
public void run() throws Exception {
@ -139,15 +144,15 @@ public record CompileTask(Project project, String[] args) implements Task {
* @throws Exception 编译或写入过程中出现异常时抛出
*/
public int execute(String[] args) throws Exception {
/* ---------------- 3.1 解析命令行参数 ---------------- */
// ---------------- 解析命令行参数 ----------------
boolean runAfterCompile = false;
String outputName = null;
Path dir = null;
List<Path> sources = new ArrayList<>();
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
String arg = args[i];
switch (arg) {
case "run" -> runAfterCompile = true;
case "--debug" -> SnowConfig.MODE = Mode.DEBUG;
case "-o" -> {
@ -167,10 +172,10 @@ public record CompileTask(Project project, String[] args) implements Task {
}
}
default -> {
if (args[i].endsWith(".snow")) {
sources.add(Path.of(args[i]));
if (arg.endsWith(".snow")) {
sources.add(Path.of(arg));
} else {
System.err.println("Unknown option or file: " + args[i]);
System.err.println("Unknown option or file: " + arg);
new CompileCommand().printUsage();
return 1;
}
@ -178,7 +183,7 @@ public record CompileTask(Project project, String[] args) implements Task {
}
}
/* ---------------- 3.2 递归收集目录中的 .snow 文件 ---------------- */
// --------- 如果指定了目录则递归收集所有 *.snow ---------
if (dir != null) {
if (!Files.isDirectory(dir)) {
System.err.println("Not a directory: " + dir);
@ -186,7 +191,7 @@ public record CompileTask(Project project, String[] args) implements Task {
}
try (var stream = Files.walk(dir)) {
stream.filter(p -> p.toString().endsWith(".snow"))
.sorted()
.sorted() // 确保稳定顺序
.forEach(sources::add);
}
}
@ -195,73 +200,87 @@ public record CompileTask(Project project, String[] args) 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 <name>");
return 1;
}
/* ---------------- 4. 词法和语法分析 ---------------- */
// ---------------- 1. 词法/语法分析并打印源代码 ----------------
List<Node> allAst = new ArrayList<>();
print("## 编译器输出");
print("### Snow 源代码");
for (Path src : sources) {
String code = Files.readString(src, StandardCharsets.UTF_8);
print("#### " + src.getFileName());
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());
print(code);
LexerEngine lex = new LexerEngine(code, src.toString());
if (!lex.getErrors().isEmpty()) return 1;
// 词法语法分析
LexerEngine lexer = new LexerEngine(code, p.toString());
// 若词法阶段存在错误立即终止编译避免进入后续的语法及语义分析
if (!lexer.getErrors().isEmpty()) {
return 1;
}
ParserContext ctx = new ParserContext(lex.getAllTokens(), src.toString());
ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
allAst.addAll(new ParserEngine(ctx).parse());
}
/* ---------------- 5. 语义分析 ---------------- */
// ---------------- 2. 语义分析 ----------------
SemanticAnalyzerRunner.runSemanticAnalysis(allAst, false);
/* ---------------- 6. AST 转 IR并调整入口 ---------------- */
// ---------------- 3. AST IR并将 main 函数移动至首位 ----------------
IRProgram program = new IRProgramBuilder().buildProgram(allAst);
program = reorderForEntry(program); // 确保 main 在首位
program = reorderForEntry(program);
// print("### AST");
// if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst);
// 打印 AST IR
print("### AST");
if (SnowConfig.isDebug()) ASTPrinter.printJson(allAst);
print("### IR");
print(program.toString());
/* ---------------- 7. IR 转 VM 指令 ---------------- */
// ---------------- 4. IR VM 指令 ----------------
VMProgramBuilder builder = new VMProgramBuilder();
List<InstructionGenerator<? extends IRInstruction>> gens =
List<InstructionGenerator<? extends IRInstruction>> generators =
InstructionGeneratorProvider.defaultGenerators();
for (IRFunction fn : program.functions()) {
Map<IRVirtualRegister, Integer> slotMap =
new RegisterAllocator().allocate(fn);
new VMCodeGenerator(slotMap, builder, gens).generate(fn);
new VMCodeGenerator(slotMap, builder, generators).generate(fn);
}
List<String> vmCode = builder.build();
List<String> finalCode = builder.build();
print("### VM code");
if (SnowConfig.isDebug()) {
for (int i = 0; i < vmCode.size(); i++) {
String[] parts = vmCode.get(i).split(" ");
for (int i = 0; i < finalCode.size(); i++) {
String[] parts = finalCode.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));
}
}
/* ---------------- 8. 写出 .water 文件 ---------------- */
Path outFile = deriveOutputPath(sources, outputName, dir);
Files.write(outFile, vmCode, StandardCharsets.UTF_8);
print("Written to " + outFile.toAbsolutePath());
// ---------------- 5. 写出 .water 文件 ----------------
Path outputFile = deriveOutputPath(sources, outputName, dir);
Files.write(outputFile, finalCode, StandardCharsets.UTF_8);
print("Written to " + outputFile.toAbsolutePath());
/* ---------------- 9. 可选运行 VM ---------------- */
// ---------------- 6. 立即运行 VM ----------------
if (runAfterCompile) {
print("\n=== Launching VM ===");
VMLauncher.main(new String[]{outFile.toString()});
VMLauncher.main(new String[]{outputFile.toString()});
print("\n=== VM exited ===");
}

View File

@ -35,6 +35,9 @@ 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
* </pre>
*
* @author (your name or org)
* @since 1.0
*/
public class RPushCommand implements Command {

View File

@ -400,20 +400,9 @@ public class SyscallCommand implements Command {
// 必须是可变 List
@SuppressWarnings("unchecked")
java.util.List<Object> mlist = (java.util.List<Object>) list;
if (idx < 0) {
if (idx < 0 || idx >= mlist.size())
throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + mlist.size() + ")");
}
// 允许自动扩容 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)