Repository: elvin-du/tinyscript Branch: master Commit: 5e109414914e Files: 76 Total size: 86.4 KB Directory structure: gitextract_72t38b4e/ ├── .gitignore ├── Makefile ├── README.md ├── gen/ │ ├── instruction.go │ ├── opcode.go │ ├── opcode_gen.go │ ├── opcode_gen_test.go │ ├── opcode_program.go │ ├── opcode_test.go │ ├── operand/ │ │ ├── immediate_number.go │ │ ├── label.go │ │ ├── offset.go │ │ ├── oprand.go │ │ ├── register.go │ │ └── types.go │ └── types.go ├── go.mod ├── go.sum ├── lexer/ │ ├── alphabet.go │ ├── alphabet_test.go │ ├── keywords.go │ ├── lexer.go │ ├── lexer_test.go │ ├── token.go │ └── util/ │ ├── stream.go │ └── stream_test.go ├── main.go ├── parser/ │ ├── ast/ │ │ ├── ast.go │ │ ├── block.go │ │ ├── expr.go │ │ ├── expr_call.go │ │ ├── factor.go │ │ ├── func_args.go │ │ ├── priority_table.go │ │ ├── program.go │ │ ├── scalar.go │ │ ├── stmt.go │ │ ├── stmt_assign.go │ │ ├── stmt_assign_test.go │ │ ├── stmt_declare.go │ │ ├── stmt_declare_test.go │ │ ├── stmt_for.go │ │ ├── stmt_func_declare.go │ │ ├── stmt_func_declare_test.go │ │ ├── stmt_if.go │ │ ├── stmt_if_test.go │ │ ├── stmt_return.go │ │ ├── stream.go │ │ ├── stream_test.go │ │ ├── type.go │ │ ├── util.go │ │ └── variable.go │ ├── parser.go │ └── parser_test.go ├── tests/ │ ├── add.ts │ ├── complex-if.ts │ ├── fact2.ts │ ├── fact5.ts │ ├── function.ts │ └── recursion.ts ├── translator/ │ ├── static_table_test.go │ ├── symbol/ │ │ ├── static_table.go │ │ ├── symbol.go │ │ ├── table.go │ │ ├── table_test.go │ │ ├── types.go │ │ └── util.go │ ├── tainstruction.go │ ├── tainstruction_type.go │ ├── taprogram.go │ ├── translator.go │ ├── translator_test.go │ ├── util.go │ └── util_test.go └── vm/ ├── vm.go └── vm_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .idea/ ./tinyscript ================================================ FILE: Makefile ================================================ test: go test ./... ================================================ FILE: README.md ================================================ # tinyscript 整个项目包括三个东西: 1. 创建了一个自己的语言 2. 编译器 3. 虚拟机 golang实现的一个编译器,用来编译一个自己创建的语言(用来玩的),最后写了一个自定义虚拟机用来运行自定义语言。 ## 语言介绍 为了跨平台(其实是为了方便开发 ^ ^),所以这个语言没有静态编译成硬件指令集,最后的机器码是我自己的定义的,和MIPS类似的(其实就是一个mips子集)虚拟指令集。为了运行这些指令集,我写了一个虚拟机。 语言和golang和javascript类似,实现了函数,类型声明,函数调用等最基本的一些语言元素,没有实现类,结构体,接口等复杂数据结构。 下面是用这个语言编程的例子: ``` func fact(int n) int { if(n == 0) { return 1 } return fact(n-1) * n } func main() void { return fact(2) } ``` 每个函数都实现了相应的UnitTest,单元测试真香~ ## 声明: 工程思路不是我自己想出来的,来自于慕课网《大学计算机必修课新讲--编译原理+操作系统+图形学》这个课程。 理论主要是看《龙书》的一部分,《自己动手写编译器,连接器》和bilibili上的中科大华保健老师的《编译原理》和哈尔滨工业大学的《编译原理》课程的一部分。 ================================================ FILE: gen/instruction.go ================================================ package gen import ( "fmt" "reflect" "strings" "tinyscript/gen/operand" "tinyscript/translator/symbol" ) const ( MASK_OPCODE = 0xfc000000 MASK_R0 = 0x03e00000 MASK_R1 = 0x001f0000 MASK_R2 = 0x0000f800 MASK_OFFSET0 = 0x03ffffff MASK_OFFSET1 = 0x001fffff MASK_OFFSET2 = 0x000007ff ) type Instruction struct { Code *OpCode OpList []operand.Operand } func NewInstruction(code *OpCode) *Instruction { return &Instruction{Code: code, OpList: make([]operand.Operand, 0)} } func NewJumpInstruction(code *OpCode, offset int) *Instruction { i := NewInstruction(code) i.AddOperand(operand.NewOffset(offset)) return i } func NewOffsetInstruction(code *OpCode, r1, r2 *operand.Register, offset *operand.Offset) *Instruction { i := NewInstruction(code) i.AddOperand(r1) i.AddOperand(r2) i.AddOperand(offset) return i } func NewRegisterInstruction(code *OpCode, r1, r2, r3 *operand.Register) *Instruction { i := NewInstruction(code) i.AddOperand(r1) if r2 != nil { i.AddOperand(r2) } if r3 != nil { i.AddOperand(r3) } return i } func NewBNEInstruction(r1, r2 *operand.Register, label string) *Instruction { i := NewInstruction(BNE) i.AddOperand(r1) i.AddOperand(r2) i.AddOperand(operand.NewLabel(label)) return i } func NewImmediateInstruction(code *OpCode, r1 *operand.Register, number *operand.ImmediateNumber) *Instruction { i := NewInstruction(code) i.AddOperand(r1) i.AddOperand(number) return i } func (i *Instruction) AddOperand(o operand.Operand) { i.OpList = append(i.OpList, o) } func (i *Instruction) String() string { s := i.Code.String() prts := make([]string, 0, len(i.OpList)+1) for _, op := range i.OpList { prts = append(prts, op.String()) } return s + " " + strings.Join(prts, " ") } func (i *Instruction) ToByteCode() int { code := 0 x := i.Code.Value code |= int(x) << 26 switch i.Code.AddrType { case ADDRESSING_TYPE_IMMEDIATE: r0 := i.OpList[0].(*operand.Register) code |= int(r0.Addr) << 21 code |= i.OpList[1].(*operand.ImmediateNumber).Value return code case ADDRESSING_TYPE_REGISTER: r1 := i.OpList[0].(*operand.Register) code |= int(r1.Addr) << 21 if len(i.OpList) > 1 { code |= int(i.OpList[1].(*operand.Register).Addr) << 16 if len(i.OpList) > 2 { r2 := int(i.OpList[2].(*operand.Register).Addr) code |= r2 << 11 } } case ADDRESSING_TYPE_JUMP: if len(i.OpList) > 0 { code |= i.OpList[0].(*operand.Label).Offset.GetEncodedOffset() } case ADDRESSING_TYPE_OFFSET: r1 := i.OpList[0].(*operand.Register) r2 := i.OpList[1].(*operand.Register) var offset *operand.Offset = nil if reflect.TypeOf(i.OpList[2]).String() == reflect.TypeOf(&operand.Label{}).String() { offset = i.OpList[2].(*operand.Label).Offset } else { offset = i.OpList[2].(*operand.Offset) } code |= int(r1.Addr) << 21 code |= int(r2.Addr) << 16 code |= offset.GetEncodedOffset() } return code } func LoadToRegister(target *operand.Register, arg *symbol.Symbol) *Instruction { //转成证书,目前只支持整数 if arg.Typ == symbol.SYMBOL_ADDRESS { return NewOffsetInstruction(LW, target, operand.SP, operand.NewOffset(-arg.Offset)) } else if arg.Typ == symbol.SYMBOL_IMMEDIATE { return NewOffsetInstruction(LW, target, operand.STATIC, operand.NewOffset(arg.Offset)) } panic(fmt.Sprintf("Cannot load type %v symbol to register", arg.Typ)) } func SaveToMemory(source *operand.Register, arg *symbol.Symbol) *Instruction { return NewOffsetInstruction(SW, source, operand.SP, operand.NewOffset(-arg.Offset)) } func FromByCode(code int) *Instruction { byteOpcode := (byte)(int(code&MASK_OPCODE) >> 26) opcode := FromByte(byteOpcode) i := NewInstruction(opcode) switch opcode.AddrType { case ADDRESSING_TYPE_IMMEDIATE: reg := (code & MASK_R0) >> 21 number := code & MASK_OFFSET1 i.OpList = append(i.OpList, operand.RegisterFromAddr(reg)) i.OpList = append(i.OpList, operand.NewImmediateNumber(number)) case ADDRESSING_TYPE_REGISTER: r1Addr := (code & MASK_R0) >> 21 r2Addr := (code & MASK_R1) >> 16 r3Addr := (code & MASK_R2) >> 11 r1 := operand.RegisterFromAddr(r1Addr) var r2 *operand.Register = nil if r2Addr != 0 { r2 = operand.RegisterFromAddr(r2Addr) } var r3 *operand.Register = nil if r3Addr != 0 { r3 = operand.RegisterFromAddr(r3Addr) } i.OpList = append(i.OpList, r1) if nil != r2 { i.OpList = append(i.OpList, r2) } if nil != r3 { i.OpList = append(i.OpList, r3) } case ADDRESSING_TYPE_JUMP: offset := code & MASK_OFFSET0 i.OpList = append(i.OpList, operand.DecodeOffset(offset)) case ADDRESSING_TYPE_OFFSET: r1Addr := (code & MASK_R0) >> 21 r2Addr := (code & MASK_R1) >> 16 offset := code & MASK_OFFSET2 i.OpList = append(i.OpList, operand.RegisterFromAddr(r1Addr)) i.OpList = append(i.OpList, operand.RegisterFromAddr(r2Addr)) i.OpList = append(i.OpList, operand.DecodeOffset(offset)) } return i } func (i *Instruction) GetOperand(index int) operand.Operand { return i.OpList[index] } ================================================ FILE: gen/opcode.go ================================================ package gen import "fmt" var Codes = [63]*OpCode{} var ( ADD = NewOpCode(ADDRESSING_TYPE_REGISTER, "ADD", 0x01) SUB = NewOpCode(ADDRESSING_TYPE_REGISTER, "SUB", 0x02) MULT = NewOpCode(ADDRESSING_TYPE_REGISTER, "MULT", 0x03) ADDI = NewOpCode(ADDRESSING_TYPE_IMMEDIATE, "ADDI", 0x05) //立即数加 SUBI = NewOpCode(ADDRESSING_TYPE_IMMEDIATE, "SUBI", 0x06) MULTI = NewOpCode(ADDRESSING_TYPE_IMMEDIATE, "MULTI", 0x07) MFLO = NewOpCode(ADDRESSING_TYPE_REGISTER, "MFLO", 0x08) //MULT/MULTI操作码的结果会存储到这个寄存器中 EQ = NewOpCode(ADDRESSING_TYPE_REGISTER, "EQ", 0x09) BNE = NewOpCode(ADDRESSING_TYPE_OFFSET, "BNE", 0x15) //不相等 SW = NewOpCode(ADDRESSING_TYPE_OFFSET, "SW", 0x10) //从寄存器写回内存 LW = NewOpCode(ADDRESSING_TYPE_OFFSET, "LW", 0x11) //从内存读入到寄存器 JUMP = NewOpCode(ADDRESSING_TYPE_JUMP, "JUMP", 0x20) JR = NewOpCode(ADDRESSING_TYPE_JUMP, "JR", 0x21) //函数的跳转 RETURN = NewOpCode(ADDRESSING_TYPE_JUMP, "RETURN", 0x22) ) type OpCode struct { Name string Value byte AddrType AddressingType } func NewOpCode(addrType AddressingType, name string, value byte) *OpCode { oc := &OpCode{Name: name, Value: value, AddrType: addrType} Codes[value] = oc return oc } func (oc *OpCode) String() string { return oc.Name } func FromByte(byteOpcode byte) *OpCode { code := Codes[byteOpcode] if nil == code { panic(fmt.Sprintf("%x opcode undefined", byteOpcode)) } return code } ================================================ FILE: gen/opcode_gen.go ================================================ package gen import ( "fmt" "tinyscript/gen/operand" "tinyscript/translator" "tinyscript/translator/symbol" ) type OpCodeGen struct { } func NewOpCodeGen() *OpCodeGen { return &OpCodeGen{} } func (g *OpCodeGen) Gen(taProgram *translator.TAProgram) *OpCodeProgram { program := NewOpCodeProgram() taInstrs := taProgram.Instructions labelHash := make(map[string]int) for _, taInstr := range taInstrs { program.AddComment(taInstr.String()) switch taInstr.Typ { case translator.TAINSTR_TYPE_ASSIGN: g.GenCopy(program, taInstr) case translator.TAINSTR_TYPE_GOTO: g.GenGoTo(program, taInstr) case translator.TAINSTR_TYPE_CALL: g.GenCall(program, taInstr) case translator.TAINSTR_TYPE_PARAM: g.GenPass(program, taInstr) case translator.TAINSTR_TYPE_SP: g.GenSP(program, taInstr) case translator.TAINSTR_TYPE_LABEL: if taInstr.Arg2 != nil && taInstr.Arg2.(string) == "main" { size := len(program.Instructions) program.SetEntry(&size) } //这里用于给计算label在代码中的行号做基础 labelHash[taInstr.Arg1.(string)] = len(program.Instructions) case translator.TAINSTR_TYPE_RETURN: g.GenReturn(program, taInstr) case translator.TAINSTR_TYPE_FUNC_BEGIN: g.GenFuncBegin(program, taInstr) case translator.TAINSTR_TYPE_IF: g.GenIf(program, taInstr) default: panic(fmt.Sprintf("unknown type %d", taInstr.Typ)) } } g.Relabel(program, labelHash) return program } func (g *OpCodeGen) GenGoTo(program *OpCodeProgram, ta *translator.TAInstruction) { label := ta.Arg1.(string) i := NewInstruction(JUMP) //label对应的未知在relabel阶段计算 i.OpList = append(i.OpList, operand.NewLabel(label)) program.Add(i) } func (g *OpCodeGen) GenIf(program *OpCodeProgram, ta *translator.TAInstruction) { label := ta.Arg2 program.Add(NewBNEInstruction(operand.S2, operand.ZERO, label.(string))) } func (g *OpCodeGen) Relabel(program *OpCodeProgram, labelMap map[string]int) { for _, instr := range program.Instructions { if instr.Code == JUMP || instr.Code == JR || instr.Code == BNE { idx := 0 if instr.Code == BNE { idx = 2 } labelOperand := instr.OpList[idx].(*operand.Label) label := labelOperand.Label offset := labelMap[label] labelOperand.Offset.Offset = offset } } } func (g *OpCodeGen) GenReturn(program *OpCodeProgram, ta *translator.TAInstruction) { ret := ta.Arg1.(*symbol.Symbol) if nil != ret { program.Add(LoadToRegister(operand.S0, ret)) } program.Add(NewOffsetInstruction(SW, operand.S0, operand.SP, operand.NewOffset(1))) i := NewInstruction(RETURN) program.Add(i) } func (g *OpCodeGen) GenSP(program *OpCodeProgram, ta *translator.TAInstruction) { offset := ta.Arg1.(int) if offset > 0 { program.Add(NewImmediateInstruction(ADDI, operand.SP, operand.NewImmediateNumber(offset))) } else { program.Add(NewImmediateInstruction(SUBI, operand.SP, operand.NewImmediateNumber(-offset))) } } func (g *OpCodeGen) GenPass(program *OpCodeProgram, ta *translator.TAInstruction) { arg1 := ta.Arg1.(*symbol.Symbol) number := ta.Arg2.(int) program.Add(LoadToRegister(operand.S0, arg1)) //pass a program.Add(NewOffsetInstruction(SW, operand.S0, operand.SP, operand.NewOffset(-number))) } func (g *OpCodeGen) GenFuncBegin(program *OpCodeProgram, ta *translator.TAInstruction) { i := NewOffsetInstruction(SW, operand.RA, operand.SP, operand.NewOffset(0)) program.Add(i) } func (g *OpCodeGen) GenCall(program *OpCodeProgram, ta *translator.TAInstruction) { label := ta.Arg1.(*symbol.Symbol) i := NewInstruction(JR) //跳转之前会把PC寄存器的值存储到RA寄存器 i.OpList = append(i.OpList, operand.NewLabel(label.Label)) program.Add(i) } func (g *OpCodeGen) GenCopy(program *OpCodeProgram, ta *translator.TAInstruction) { result := ta.Result op := ta.Op arg1 := ta.Arg1.(*symbol.Symbol) if nil == ta.Arg2 { program.Add(LoadToRegister(operand.S0, arg1)) program.Add(SaveToMemory(operand.S0, result)) } else { program.Add(LoadToRegister(operand.S0, arg1)) arg2 := ta.Arg2.(*symbol.Symbol) program.Add(LoadToRegister(operand.S1, arg2)) switch op { case "+": program.Add(NewRegisterInstruction(ADD, operand.S2, operand.S0, operand.S1)) case "-": program.Add(NewRegisterInstruction(SUB, operand.S2, operand.S0, operand.S1)) case "*": program.Add(NewRegisterInstruction(MULT, operand.S0, operand.S1, nil)) program.Add(NewRegisterInstruction(MFLO, operand.S2, nil, nil)) case "==": program.Add(NewRegisterInstruction(EQ, operand.S2, operand.S1, operand.S0)) } program.Add(SaveToMemory(operand.S2, result)) } } ================================================ FILE: gen/opcode_gen_test.go ================================================ package gen import ( "github.com/magiconair/properties/assert" "testing" "tinyscript/parser" "tinyscript/translator" ) func TestExprEvaluate(t *testing.T) { source := "var a = 3 * 2*(5+1)" node := parser.Parse(source) taprog := translator.NewTranslator().Translate(node) assert.Equal(t, taprog.StaticTable.String(), `0:3 1:2 2:5 3:1`) g := NewOpCodeGen() prog := g.Gen(taprog) expected := `#p0 = 5 + 1 LW S0 STATIC 2 LW S1 STATIC 3 ADD S2 S0 S1 SW S2 SP -1 #p1 = 2 * p0 LW S0 STATIC 1 LW S1 SP -1 MULT S0 S1 MFLO S2 SW S2 SP -2 #p2 = 3 * p1 LW S0 STATIC 0 LW S1 SP -2 MULT S0 S1 MFLO S2 SW S2 SP -3 #a = p2 LW S0 SP -3 SW S0 SP 0` assert.Equal(t, prog.String(), expected) } func TestFuncEvaluate(t *testing.T) { node := parser.ParseFromFile("../tests/add.ts") taprog := translator.NewTranslator().Translate(node) g := NewOpCodeGen() prog := g.Gen(taprog) expected := `#FUNC_BEGIN SW RA SP 0 #p1 = a + b LW S0 SP -1 LW S1 SP -2 ADD S2 S0 S1 SW S2 SP -3 #RETURN p1 LW S0 SP -3 SW S0 SP 1 RETURN #FUNC_BEGIN MAIN:SW RA SP 0 #PARAM 10 3 LW S0 STATIC 0 SW S0 SP -3 #PARAM 20 4 LW S0 STATIC 1 SW S0 SP -4 #SP -2 SUBI SP 2 #CALL L0 JR L0 #SP 2 ADDI SP 2 #RETURN SW S0 SP 1 RETURN #SP -1 SUBI SP 1 #CALL L1 JR L1 #SP 1 ADDI SP 1` assert.Equal(t,prog.String(),expected) } ================================================ FILE: gen/opcode_program.go ================================================ package gen import ( "strconv" "strings" "tinyscript/translator" ) type OpCodeProgram struct { Entry *int Instructions []*Instruction Comments map[int]string //注释;行号:注释内容 } func NewOpCodeProgram() *OpCodeProgram { return &OpCodeProgram{Entry: nil, Instructions: make([]*Instruction, 0), Comments: make(map[int]string)} } func (o *OpCodeProgram) Add(instr *Instruction) { o.Instructions = append(o.Instructions, instr) } func (o *OpCodeProgram) String() string { prts := make([]string, 0, len(o.Instructions)) for i, instr := range o.Instructions { if c, ok := o.Comments[i]; ok { prts = append(prts, "#"+c) } str := instr.String() if o.Entry != nil && *o.Entry == i { str = "MAIN:" + str } prts = append(prts, str) } return strings.Join(prts, "\n") } func (o *OpCodeProgram) SetEntry(entry *int) { o.Entry = entry } //当前指令的位置添加一行注释 func (o *OpCodeProgram) AddComment(comment string) { o.Comments[len(o.Instructions)] = comment } func (o *OpCodeProgram) ToByteCode() []int { codes := []int{} for _, instr := range o.Instructions { codes = append(codes, instr.ToByteCode()) } return codes } //从三地址代码中获取静态符号表中的值,存起来在虚拟机实例化时写入内存静态区 func (o *OpCodeProgram) GetStaticArea(taProgram *translator.TAProgram) []int { l := []int{} for _, symbol := range taProgram.StaticTable.Symbols { i, err := strconv.Atoi(symbol.Lexeme.Value) if nil != err { panic(err) } l = append(l, i) } return l } ================================================ FILE: gen/opcode_test.go ================================================ package gen import ( "github.com/magiconair/properties/assert" "reflect" "testing" "tinyscript/gen/operand" symbol2 "tinyscript/translator/symbol" ) func TestAdd(t *testing.T) { a := NewInstruction(ADD) a.AddOperand(operand.S2) a.AddOperand(operand.S0) a.AddOperand(operand.S1) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestMult(t *testing.T) { a := NewInstruction(MULT) a.AddOperand(operand.S0) a.AddOperand(operand.S1) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestNewJumpInstruction(t *testing.T) { a := NewInstruction(JUMP) label := operand.NewLabel("L0") a.AddOperand(label) label.SetOffset(100) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestJR(t *testing.T) { a := NewInstruction(JR) label := operand.NewLabel("L0") a.AddOperand(label) label.SetOffset(100) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestSW(t *testing.T) { symbol := symbol2.NewSymbol(symbol2.SYMBOL_IMMEDIATE) symbol.Offset = -100 a := SaveToMemory(operand.S0, symbol) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestSW1(t *testing.T) { symbol := symbol2.NewSymbol(symbol2.SYMBOL_IMMEDIATE) symbol.Offset = 100 a := SaveToMemory(operand.S0, symbol) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestLW(t *testing.T) { symbol := symbol2.NewSymbol(symbol2.SYMBOL_IMMEDIATE) symbol.Offset = 100 a := LoadToRegister(operand.S0, symbol) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestLW2(t *testing.T) { symbol := symbol2.NewSymbol(symbol2.SYMBOL_ADDRESS) symbol.Offset = 100 a := LoadToRegister(operand.S0, symbol) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestSP(t *testing.T) { a := NewImmediateInstruction(ADDI, operand.SP, operand.NewImmediateNumber(100)) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func TestBNE(t *testing.T) { a := NewBNEInstruction(operand.S0, operand.S1, "L0") a.GetOperand(2).(*operand.Label).SetOffset(100) AssertSameInstruction(t, a, FromByCode(a.ToByteCode())) } func AssertSameInstruction(t *testing.T, a, b *Instruction) { assert.Equal(t, a.Code, b.Code) assert.Equal(t, len(a.OpList), len(b.OpList)) for i, av := range a.OpList { bv := b.GetOperand(i) if reflect.ValueOf(av).Type().String() == reflect.TypeOf(&operand.Label{}).String() { assert.Equal(t, bv, av.(*operand.Label).Offset) }else { assert.Equal(t, bv, av) } if reflect.ValueOf(av).Type().String() == reflect.TypeOf(&operand.ImmediateNumber{}).String() { assert.Equal(t, av.(*operand.ImmediateNumber).Value, bv.(*operand.ImmediateNumber).Value) } else if reflect.ValueOf(av).Type().String() == reflect.TypeOf(&operand.Offset{}).String() { assert.Equal(t, av.(*operand.Offset).Offset, bv.(*operand.Offset).Offset) } else if reflect.ValueOf(av).Type().String() == reflect.TypeOf(&operand.Register{}).String() { assert.Equal(t, av.(*operand.Register).Addr, bv.(*operand.Register).Addr) assert.Equal(t, av.(*operand.Register).Name, bv.(*operand.Register).Name) } else if reflect.ValueOf(av).Type().String() == reflect.TypeOf(&operand.Label{}).String() { assert.Equal(t, av.(*operand.Label).Offset.Offset, bv.(*operand.Offset).Offset) } else { panic("unsupported encode/decode type" + av.String()) } } } ================================================ FILE: gen/operand/immediate_number.go ================================================ package operand import "fmt" var _ Operand = &ImmediateNumber{} type ImmediateNumber struct { Value int } func NewImmediateNumber(value int) *ImmediateNumber { return &ImmediateNumber{Value: value} } func (i *ImmediateNumber) String() string { return fmt.Sprintf("%d", i.Value) } func (*ImmediateNumber) Typ() OperandType { return TYPE_IMMEDIATE } ================================================ FILE: gen/operand/label.go ================================================ package operand var _ Operand = &Label{} type Label struct { Label string *Offset } func NewLabel(label string) *Label { return &Label{Label: label, Offset: NewOffset(0)} } func (l *Label) String() string { return l.Label } func (*Label) Typ() OperandType { return TYPE_LABEL } func (l *Label) SetOffset(offset int) { l.Offset = NewOffset(offset) } ================================================ FILE: gen/operand/offset.go ================================================ package operand import "fmt" var _ Operand = &Offset{} type Offset struct { Offset int } func NewOffset(offset int) *Offset { return &Offset{Offset: offset} } func (o *Offset) String() string { return fmt.Sprintf("%d", o.Offset) } func (o *Offset) GetEncodedOffset() int { if o.Offset > 0 { return o.Offset } return 0x400 | -o.Offset } func DecodeOffset(offset int) *Offset { if offset&0x400 > 0 { offset = offset & 0x3ff offset = -offset } return NewOffset(offset) } func (*Offset) Typ() OperandType { return TYPE_OFFSET } ================================================ FILE: gen/operand/oprand.go ================================================ package operand type Operand interface { String() string Typ() OperandType } ================================================ FILE: gen/operand/register.go ================================================ package operand import "fmt" var _ Operand = &Register{} var ( Registers = [31]*Register{} ZERO = NewRegister("ZERO", 1) PC = NewRegister("PC", 2) SP = NewRegister("SP", 3) STATIC = NewRegister("STATIC", 4) RA = NewRegister("RA", 5) S0 = NewRegister("S0", 10) S1 = NewRegister("S1", 11) S2 = NewRegister("S2", 12) L0 = NewRegister("L0", 20) ) type Register struct { Addr byte Name string } func NewRegister(name string, addr byte) *Register { reg := &Register{Addr: addr, Name: name} Registers[addr] = reg return reg } func (reg *Register) Typ() OperandType { return TYPE_REGISTER } func (reg *Register) String() string { return reg.Name } func RegisterFromAddr(reg int) *Register { if reg < 0 || reg >= len(Registers) { panic(fmt.Sprintf("no register's address is %d", reg)) } return Registers[reg] } ================================================ FILE: gen/operand/types.go ================================================ package operand type OperandType int const ( TYPE_REGISTER = iota TYPE_IMMEDIATE TYPE_LABEL TYPE_OFFSET ) ================================================ FILE: gen/types.go ================================================ package gen //寻址类型 type AddressingType int const ( ADDRESSING_TYPE_IMMEDIATE AddressingType = iota ADDRESSING_TYPE_REGISTER ADDRESSING_TYPE_JUMP ADDRESSING_TYPE_BRANCH ADDRESSING_TYPE_OFFSET ) ================================================ FILE: go.mod ================================================ module tinyscript go 1.14 require github.com/magiconair/properties v1.8.1 ================================================ FILE: go.sum ================================================ github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= ================================================ FILE: lexer/alphabet.go ================================================ package lexer import "regexp" var ( ptnLetter = regexp.MustCompile("^[a-zA-Z]$") ptnNumber = regexp.MustCompile("^[0-9]$") ptnLiteral = regexp.MustCompile("^[_a-zA-Z0-9]$") ptnOperator = regexp.MustCompile("^[+-\\\\*<>=!&|^%/]$") ) func IsLetter(c string) bool { return ptnLetter.MatchString(c) } func IsNumber(c string) bool { return ptnNumber.MatchString(c) } func IsLiteral(c string) bool { return ptnLiteral.MatchString(c) } func IsOperator(c string) bool { return ptnOperator.MatchString(c) } ================================================ FILE: lexer/alphabet_test.go ================================================ package lexer import ( "github.com/magiconair/properties/assert" "testing" ) func TestAlphabet(t *testing.T) { assert.Equal(t, IsLetter("a"), true) assert.Equal(t, IsLiteral("a"), true) assert.Equal(t, IsNumber("2"), true) assert.Equal(t, IsOperator("*"), true) assert.Equal(t, IsOperator("^"), true) assert.Equal(t, IsOperator("-"), true) assert.Equal(t, IsOperator("="), true) assert.Equal(t, IsOperator("/"), true) assert.Equal(t, IsOperator("%"), true) } ================================================ FILE: lexer/keywords.go ================================================ package lexer var KeyWords = map[string]bool{ "var": true, "if": true, "else": true, "for": true, "while": true, "break": true, "func": true, "return": true, } func IsKeyword(key string) bool { return KeyWords[key] } ================================================ FILE: lexer/lexer.go ================================================ package lexer import ( "bytes" "io" "os" "path/filepath" "tinyscript/lexer/util" ) const EndToken = "$" type Lexer struct { *util.Stream endToken string } func FromFile(path string) []*Token { absPath, err := filepath.Abs(path) if nil != err { panic(err) } f, err := os.Open(absPath) if nil != err { panic(err) } defer f.Close() return NewLexer(f, EndToken).Analyse() } func Analyse(source string) []*Token { return NewLexer(bytes.NewBufferString(source), EndToken).Analyse() } func NewLexer(r io.Reader, et string) *Lexer { s := util.NewStream(r, EndToken) return &Lexer{Stream: s, endToken: et} } func (l *Lexer) Analyse() []*Token { tokens := make([]*Token, 0) for ; l.HasNext(); { c := l.Next() if c == EndToken { break } lookahead := l.Peek() if c == " " || c == "\n" || c == "\t" { continue } if "/" == c { if lookahead == "/" { for ; l.HasNext(); { if "\n" == l.Next() { break } } } else if lookahead == "*" { valid := false for ; l.HasNext(); { p := l.Next() if "*" == p && l.Peek() == "/" { l.Next() valid = true break } } if !valid { panic("source comment invalid") } } continue } if c == "{" || c == "}" || c == "(" || c == ")" { tokens = append(tokens, NewToken(BRACKET, c)) continue } if c == `"` || c == `'` { l.PutBack(c) tokens = append(tokens, l.MakeString()) continue } if IsLetter(c) { l.PutBack(c) tokens = append(tokens, l.MakeVarOrKeyword()) continue } if IsNumber(c) { l.PutBack(c) tokens = append(tokens, l.MakeNumber()) continue } //+ - . //+-: 3+5, +5, 3 * -5 if (c == "+" || c == "-" || c == ".") && IsNumber(lookahead) { var lastToken *Token = nil if len(tokens) > 0 { lastToken = tokens[len(tokens)-1] } if nil == lastToken || !lastToken.IsValue() || lastToken.IsOperator() { l.PutBack(c) tokens = append(tokens, l.MakeNumber()) continue } } if IsOperator(c) { l.PutBack(c) tokens = append(tokens, l.MakeOp()) continue } panic("unexpected character" + c) } return tokens } func (l *Lexer) MakeString() *Token { s := "" state := 0 for ; l.HasNext(); { c := l.Next() switch state { case 0: if c == `'` { state = 1 } else { state = 2 } s += c case 1: if `'` == c { return NewToken(STRING, s+c) } else { s += c } case 2: if `"` == c { return NewToken(STRING, s+c) } else { s += c } } } panic("make string failed") } func (l *Lexer) MakeVarOrKeyword() *Token { s := "" for ; l.HasNext(); { lookahead := l.Peek() if IsLiteral(lookahead) { s += lookahead } else { break } l.Next() } if IsKeyword(s) { return NewToken(KEYWORD, s) } if "true" == s || "false" == s { return NewToken(BOOLEAN, s) } return NewToken(VARIABLE, s) } func (l *Lexer) MakeOp() *Token { state := 0 for ; l.HasNext(); { lookahead := l.Next() switch state { case 0: switch lookahead { case "+": state = 1 case "-": state = 2 case "*": state = 3 case `/`: state = 4 case `>`: state = 5 case `<`: state = 6 case `=`: state = 7 case `!`: state = 8 case `&`: state = 9 case `|`: state = 10 case `^`: state = 11 case `%`: state = 12 case ",": return NewToken(OPERATOR, ",") case ";": return NewToken(OPERATOR, ";") } case 1: switch lookahead { case `+`: return NewToken(OPERATOR, "++") case `=`: return NewToken(OPERATOR, "+=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "+") } case 2: switch lookahead { case `-`: return NewToken(OPERATOR, "--") case `=`: return NewToken(OPERATOR, "-=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "-") } case 3: switch lookahead { case `=`: return NewToken(OPERATOR, "*=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "*") } case 4: switch lookahead { case `=`: return NewToken(OPERATOR, "/=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "/") } case 5: switch lookahead { case `=`: return NewToken(OPERATOR, ">=") case `>`: return NewToken(OPERATOR, ">>") default: l.PutBack(lookahead) return NewToken(OPERATOR, ">") } case 6: switch lookahead { case `=`: return NewToken(OPERATOR, "<=") case `<`: return NewToken(OPERATOR, "<<") default: l.PutBack(lookahead) return NewToken(OPERATOR, "<") } case 7: switch lookahead { case `=`: return NewToken(OPERATOR, "==") default: l.PutBack(lookahead) return NewToken(OPERATOR, "=") } case 8: switch lookahead { case `=`: return NewToken(OPERATOR, "!=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "!") } case 9: switch lookahead { case `&`: return NewToken(OPERATOR, "&&") case `=`: return NewToken(OPERATOR, "&=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "&") } case 10: switch lookahead { case `|`: return NewToken(OPERATOR, "||") case `=`: return NewToken(OPERATOR, "|=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "|") } case 11: switch lookahead { case `^`: return NewToken(OPERATOR, "^^") case `=`: return NewToken(OPERATOR, "^=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "^") } case 12: switch lookahead { case `=`: return NewToken(OPERATOR, "%=") default: l.PutBack(lookahead) return NewToken(OPERATOR, "%") } } } panic("makeOp failed") } func (l *Lexer) MakeNumber() *Token { state := 0 s := "" for ; l.HasNext(); { lookahead := l.Peek() switch state { case 0: if "0" == lookahead { state = 1 } else if IsNumber(lookahead) { state = 2 } else if `+` == lookahead || `-` == lookahead { state = 3 } else if lookahead == `.` { state = 5 } case 1: if lookahead == "0" { state = 1 } else if IsNumber(lookahead) { state = 2 } else if lookahead == "." { state = 4 } else { return NewToken(INTEGER, s) } case 2: if IsNumber(lookahead) { state = 2 } else if lookahead == "." { state = 4 } else { return NewToken(INTEGER, s) } case 3: if IsNumber(lookahead) { state = 2 } else if lookahead == "." { state = 5 } else { panic("unexpected character " + lookahead) } case 4: if "." == lookahead { panic("unexpected character" + lookahead) } else if IsNumber(lookahead) { state = 20 } else { return NewToken(FLOAT, s) } case 5: if IsNumber(lookahead) { state = 20 } else { panic("unexpected character" + lookahead) } case 20: if IsNumber(lookahead) { state = 20 } else if "." == lookahead { panic("unexpected character" + lookahead) } else { return NewToken(FLOAT, s) } } l.Next() s += lookahead } panic("makeNumber failed") } ================================================ FILE: lexer/lexer_test.go ================================================ package lexer import ( "bytes" "github.com/magiconair/properties/assert" "regexp" "strings" "testing" ) func TestLexer_MakeVarOrKeyword(t *testing.T) { l := NewLexer(bytes.NewBufferString("if abc"), "$") token := l.MakeVarOrKeyword() token2 := NewLexer(bytes.NewBufferString("true abc"), EndToken).MakeVarOrKeyword() assert.Equal(t, token.Typ, KEYWORD) assert.Equal(t, token.Value, "if") assert.Equal(t, token2.Typ, BOOLEAN) assert.Equal(t, token2.Value, "true") l.Next() token3 := l.MakeVarOrKeyword() assert.Equal(t, token3.Typ, VARIABLE) assert.Equal(t, token3.Value, "abc") } func TestLexer_MakeString(t *testing.T) { token := NewLexer(bytes.NewBufferString(`"123"`), "$").MakeString() assert.Equal(t, token.Typ, STRING) assert.Equal(t, token.Value, `"123"`) } func TestLexer_MakeOp(t *testing.T) { tests := []string{ "+ xxx", "++mmm", "/=g", "==1", "&=3434", "&8888", "||xxxx", "^=111", "%79", } for _, test := range tests { token := NewLexer(bytes.NewBufferString(test), "$").MakeOp() assert.Equal(t, token.Typ, OPERATOR) } } func TestLexer_MakeNumber(t *testing.T) { tests := []string{ "+0 aa", "-0 aa", ".3000 aa", ".55 ww", "778.99 aa", "355 kkk", "-888*234aa", } for _, test := range tests { token := NewLexer(bytes.NewBufferString(test), "$").MakeNumber() value := regexp.MustCompile("[* ]+").Split(token.Value, 1)[0] //t.Log(value) assert.Equal(t, token.Value, value) if strings.Contains(token.Value, ".") { assert.Equal(t, token.Typ, FLOAT) } else { assert.Equal(t, token.Typ, INTEGER) } } } func TestLexer_Analyse(t *testing.T) { source := `(w+c)^100.12==+100-30eee` lexer := NewLexer(bytes.NewBufferString(source), EndToken) tokens := lexer.Analyse() assert.Equal(t, len(tokens), 12) assert.Equal(t, tokens[0].Value, "(") assert.Equal(t, tokens[1].Value, "w") assert.Equal(t, tokens[2].Value, "+") assert.Equal(t, tokens[3].Value, "c") assert.Equal(t, tokens[4].Value, ")") assert.Equal(t, tokens[5].Value, "^") assert.Equal(t, tokens[6].Value, "100.12") assert.Equal(t, tokens[7].Value, "==") assert.Equal(t, tokens[8].Value, "+100") assert.Equal(t, tokens[9].Value, "-") assert.Equal(t, tokens[10].Value, "30") assert.Equal(t, tokens[11].Value, "eee") } func Test_Function(t *testing.T) { source := `func foo(a,b){ print(a+b) } foo(-100.0,100)` lexer := NewLexer(bytes.NewBufferString(source), EndToken) tokens := lexer.Analyse() assertToken(t, tokens[0], "func", KEYWORD) assertToken(t, tokens[1], "foo", VARIABLE) assertToken(t, tokens[2], "(", BRACKET) assertToken(t, tokens[3], "a", VARIABLE) assertToken(t, tokens[4], ",", OPERATOR) assertToken(t, tokens[5], "b", VARIABLE) assertToken(t, tokens[6], ")", BRACKET) assertToken(t, tokens[7], "{", BRACKET) assertToken(t, tokens[8], "print", VARIABLE) assertToken(t, tokens[9], "(", BRACKET) assertToken(t, tokens[10], "a", VARIABLE) assertToken(t, tokens[11], "+", OPERATOR) assertToken(t, tokens[12], "b", VARIABLE) assertToken(t, tokens[13], ")", BRACKET) assertToken(t, tokens[14], "}", BRACKET) assertToken(t, tokens[15], "foo", VARIABLE) assertToken(t, tokens[16], "(", BRACKET) assertToken(t, tokens[17], "-100.0", FLOAT) assertToken(t, tokens[18], ",", OPERATOR) assertToken(t, tokens[19], "100", INTEGER) assertToken(t, tokens[20], ")", BRACKET) } func TestDeleteComment(t *testing.T) { source := `/*12324abdfda 34fa9kfjl*/a=1 ` lexer := NewLexer(bytes.NewBufferString(source), EndToken) tokens := lexer.Analyse() assert.Equal(t, len(tokens), 3) } func assertToken(t *testing.T, token *Token, wantValue string, wantType TokenType) { assert.Equal(t, token.Typ, wantType) assert.Equal(t, token.Value, wantValue) } func TestFromFile(t *testing.T) { tokens := FromFile("./../tests/function.ts") assert.Equal(t, len(tokens), 16) } ================================================ FILE: lexer/token.go ================================================ package lexer import "fmt" type TokenType int const ( KEYWORD TokenType = 1 VARIABLE TokenType = 2 OPERATOR TokenType = 3 BRACKET TokenType = 4 STRING TokenType = 5 FLOAT TokenType = 6 BOOLEAN TokenType = 7 INTEGER TokenType = 8 ) func (tt TokenType) String() string { switch tt { case KEYWORD: return "keyword" case VARIABLE: return "variable" case OPERATOR: return "operator" case BRACKET: return "bracket" case STRING: return "string`" case FLOAT: return "float" case BOOLEAN: return "boolean" case INTEGER: return "integer" } panic("unexpected token type") } type Token struct { Typ TokenType Value string } func NewToken(t TokenType, v string) *Token { return &Token{Typ: t, Value: v} } func (t *Token) IsVariable() bool { return t.Typ == VARIABLE } func (t *Token) IsScalar() bool { return t.Typ == FLOAT || t.Typ == BOOLEAN || t.Typ == INTEGER || t.Typ == STRING } func (t *Token) IsNumber() bool { return t.Typ == INTEGER || t.Typ == FLOAT } func (t *Token) IsOperator() bool { return t.Typ == OPERATOR } func (t *Token) String() string { return fmt.Sprintf("type:%v,value:%s", t.Typ, t.Value) } func (t *Token) IsValue() bool { return t.IsVariable() || t.IsScalar() } func (t *Token) IsType() bool { switch t.Value { case "bool", "int", "float", "void", "string": return true } return false } ================================================ FILE: lexer/util/stream.go ================================================ package util import ( "bufio" "container/list" "io" ) type Stream struct { scanner *bufio.Scanner queueCache *list.List endToken string isEnd bool } func NewStream(r io.Reader, et string) *Stream { s := bufio.NewScanner(r) s.Split(bufio.ScanRunes) return &Stream{scanner: s, queueCache: list.New(), endToken: et, isEnd: false} } func (s *Stream) Next() string { if s.queueCache.Len() != 0 { e := s.queueCache.Front() return s.queueCache.Remove(e).(string) } if s.scanner.Scan() { return s.scanner.Text() } s.isEnd = true return s.endToken } func (s *Stream) HasNext() bool { if s.queueCache.Len() != 0 { return true } if s.scanner.Scan() { s.queueCache.PushBack(s.scanner.Text()) return true } if !s.isEnd { return true } return false } func (s *Stream) Peek() string { if s.queueCache.Len() != 0 { return s.queueCache.Front().Value.(string) } if s.scanner.Scan() { e := s.scanner.Text() s.queueCache.PushBack(e) return e } return s.endToken } func (s *Stream) PutBack(e string) { s.queueCache.PushFront(e) } ================================================ FILE: lexer/util/stream_test.go ================================================ package util import ( "bytes" "github.com/magiconair/properties/assert" "testing" ) func TestNewStream(t *testing.T) { str := "abcd" s := NewStream(bytes.NewReader([]byte(str)), "$") assert.Equal(t, s.Next(), "a") assert.Equal(t, s.Next(), "b") assert.Equal(t, s.Peek(), "c") assert.Equal(t, s.Peek(), "c") s.PutBack("b") assert.Equal(t, s.Peek(), "b") assert.Equal(t, s.Next(), "b") assert.Equal(t, s.Next(), "c") assert.Equal(t, s.HasNext(), true, "hasnext failed") assert.Equal(t, s.Next(), "d") assert.Equal(t, s.Next(), "$") assert.Equal(t, s.Next(), "$") assert.Equal(t, s.HasNext(), false, "hasnext failed") } ================================================ FILE: main.go ================================================ package main func main() { } ================================================ FILE: parser/ast/ast.go ================================================ package ast import ( "fmt" "strings" "tinyscript/lexer" ) type ASTNode interface { //get Lexeme() *lexer.Token //ast节点对应的token是什么 Type() NodeType Label() string //用字符串标识ast节点的含义,主要用于打印日志 Children() []ASTNode GetChild(uint) ASTNode Parent() ASTNode Print(indent int) TypeLexeme() *lexer.Token //标识变量的类型和函数的返回值类型;别的ast节点没有必要设置这个属性 IsValueType() bool Prop(string) interface{} //set AddChild(ASTNode) SetLexeme(*lexer.Token) SetTypeLexeme(*lexer.Token) SetType(NodeType) SetLabel(string) SetParent(ASTNode) SetProp(string, interface{}) } type node struct { parent ASTNode children []ASTNode label string //备注(标签) typ NodeType lexeme *lexer.Token //词法单元 typeLexeme *lexer.Token //func foo(int a); 这时typelexeme等于int型的token prop map[string]interface{} //用于符号表,语法分析不会用到 } //test var _ ASTNode = &node{} func MakeNode() *node { return &node{children: make([]ASTNode, 0), prop: make(map[string]interface{})} } func (n *node) Prop(key string) interface{} { return n.prop[key] } func (n *node) SetProp(key string, value interface{}) { n.prop[key] = value } func (n *node) Lexeme() *lexer.Token { return n.lexeme } func (n *node) TypeLexeme() *lexer.Token { return n.typeLexeme } func (n *node) IsValueType() bool { return n.typ == ASTNODE_TYPE_VARIABLE || n.typ == ASTNODE_TYPE_SCALAR } func (n *node) Type() NodeType { return n.typ } func (n *node) Label() string { return n.label } func (n *node) Children() []ASTNode { return n.children } func (n *node) GetChild(index uint) ASTNode { if int(index) >= len(n.children) { return nil } return n.children[index] } func (n *node) Parent() ASTNode { return n.parent } func (n *node) AddChild(node ASTNode) { node.SetParent(n) n.children = append(n.children, node) } func (n *node) SetLexeme(lexeme *lexer.Token) { n.lexeme = lexeme } func (n *node) SetTypeLexeme(lexeme *lexer.Token) { n.typeLexeme = lexeme } func (n *node) SetType(t NodeType) { n.typ = t } func (n *node) SetLabel(str string) { n.label = str } func (n *node) SetParent(node ASTNode) { n.parent = node } func (n *node) Print(indent int) { fmt.Printf("%s%s\n", strings.Repeat(" ", indent*2), n.label) for _, child := range n.children { child.Print(indent + 2) } } ================================================ FILE: parser/ast/block.go ================================================ package ast var DefaultBlock ASTNode = MakeBlock() type Block struct { *Stmt } func MakeBlock() *Block { b := &Block{MakeStmt()} b.SetType(ASTNODE_TYPE_BLOCK) b.SetLabel("block") return b } func BlockParse(stream *PeekTokenStream) ASTNode { stream.NextMatch("{") block := MakeBlock() for stmt := StmtParse(stream); nil != stmt; { block.AddChild(stmt) stmt = StmtParse(stream) } stream.NextMatch("}") return block } ================================================ FILE: parser/ast/expr.go ================================================ package ast import ( "tinyscript/lexer" ) type Expr struct { *node } func MakeExpr() *Expr { e := &Expr{MakeNode()} return e } func NewExpr(typ NodeType, token *lexer.Token) *Expr { expr := MakeExpr() expr.SetType(typ) expr.SetLexeme(token) expr.SetLabel(token.Value) return expr } type ExprHOF func() ASTNode //left: E(k) -> E(k) op(k) E(k+1) | E(k+1) //right: // E(k) -> E(k+1) E_(k) // E_(k) -> op(k) E(k+1) E_(k) | ⍷ // 最高优先级: // E(t) -> F E_(k) | U E_(k) // E_(t) -> op(t) E(t) E_(t) | ⍷ func E(stream *PeekTokenStream, k int) ASTNode { if k < PriorityTable.Size()-1 { return combine( stream, func() ASTNode { return E(stream, k+1) }, func() ASTNode { return E_(stream, k) }, ) } return race( stream, func() ASTNode { return combine( stream, func() ASTNode { return F(stream) }, func() ASTNode { return E_(stream, k) }, ) }, func() ASTNode { return combine( stream, func() ASTNode { return U(stream) }, func() ASTNode { return E_(stream, k) }, ) }, ) } func U(stream *PeekTokenStream) ASTNode { token := stream.Peek() value := token.Value if value == "(" { stream.NextMatch("(") expr := E(stream, 0) stream.NextMatch(")") return expr } else if value == "++" || value == "--" || value == "!" { t := stream.Peek() stream.NextMatch(value) unaryExpr := NewExpr(ASTNODE_TYPE_UNARY_EXPR, t) unaryExpr.AddChild(E(stream, 0)) return unaryExpr } return nil } func F(stream *PeekTokenStream) ASTNode { factor := FactorParse(stream) if nil == factor { return nil } if stream.HasNext() && stream.Peek().Value == "(" { return CallExprParse(factor, stream) } return factor } func E_(stream *PeekTokenStream, k int) ASTNode { token := stream.Peek() value := token.Value if PriorityTable.IsContain(k, value) { expr := NewExpr(ASTNODE_TYPE_BINARY_EXPR, stream.NextMatch(value)) expr.AddChild( combine( stream, func() ASTNode { return E(stream, k+1) }, func() ASTNode { return E_(stream, k) }, ), ) return expr } return nil } func race(stream *PeekTokenStream, af ExprHOF, bf ExprHOF) ASTNode { if !stream.HasNext() { return nil } a := af() if nil != a { return a } return bf() } func combine(stream *PeekTokenStream, af ExprHOF, bf ExprHOF) ASTNode { a := af() if nil == a { if stream.HasNext() { return bf() } return nil } var b ASTNode = nil if stream.HasNext() { b = bf() if nil == b { return a } } else { return a } expr := NewExpr(ASTNODE_TYPE_BINARY_EXPR, b.Lexeme()) expr.AddChild(a) expr.AddChild(b.GetChild(0)) return expr } func ExprParse(stream *PeekTokenStream) ASTNode { return E(stream, 0) } ================================================ FILE: parser/ast/expr_call.go ================================================ package ast var _ ASTNode = &CallExpr{} type CallExpr struct { *node } func MakeCallExpr() *CallExpr { e := &CallExpr{MakeNode()} e.SetType(ASTNODE_TYPE_CALL_EXPR) e.SetLabel("call") return e } func CallExprParse(factor ASTNode, stream *PeekTokenStream) ASTNode { expr := MakeCallExpr() expr.AddChild(factor) stream.NextMatch("(") for p := ExprParse(stream); p != nil; p = ExprParse(stream) { expr.AddChild(p) if stream.Peek().Value != ")" { stream.NextMatch(",") } } stream.NextMatch(")") return expr } ================================================ FILE: parser/ast/factor.go ================================================ package ast import ( "tinyscript/lexer" ) var _ ASTNode = &Factor{} type Factor struct { *node } func MakeFactor() *Factor { return &Factor{MakeNode()} } func NewFactor(stream *PeekTokenStream) *Factor { factor := &Factor{MakeNode()} token := stream.Next() factor.SetLexeme(token) factor.SetLabel(token.Value) if lexer.VARIABLE == token.Typ { factor.SetType(ASTNODE_TYPE_VARIABLE) } else { factor.SetType(ASTNODE_TYPE_SCALAR) } return factor } func FactorParse(stream *PeekTokenStream) ASTNode { token := stream.Peek() typ := token.Typ if lexer.VARIABLE == typ { stream.Next() v := MakeVariable() v.SetLabel(token.Value) v.SetLexeme(token) return v } else if token.IsScalar() { stream.Next() scalar := MakeScalar() scalar.SetLabel(token.Value) scalar.SetLexeme(token) return scalar } return nil } ================================================ FILE: parser/ast/func_args.go ================================================ package ast var _ ASTNode = &Factor{} type FuncArgs struct { *node } func MakeFuncArgs() *FuncArgs { s := &FuncArgs{MakeNode()} s.SetLabel("args") return s } func FuncArgsParse(stream *PeekTokenStream) ASTNode { args := MakeFuncArgs() for stream.Peek().IsType() { typ := stream.Next() v := FactorParse(stream) v.SetTypeLexeme(typ) //为语义分析做准备,设置参数变量的类型 args.AddChild(v) if stream.Peek().Value != ")" { stream.NextMatch(",") } } return args } ================================================ FILE: parser/ast/priority_table.go ================================================ package ast var PriorityTable = NewPriorityTable() type priorityTable struct { table [][]string } func NewPriorityTable() *priorityTable { return &priorityTable{[][]string{ []string{"&", "|", "^"}, []string{"==", "!=", ">", "<", ">=", "<="}, []string{"+", "-"}, []string{"*", "/"}, []string{"<<", ">>"}, }} } func (pt *priorityTable) Size() int { return len(pt.table) } func (pt *priorityTable) Get(level int) []string { return pt.table[level] } func (pt *priorityTable) IsContain(level int, key string) bool { strs := pt.Get(level) for _, str := range strs { if str == key { return true } } return false } ================================================ FILE: parser/ast/program.go ================================================ package ast var _ ASTNode = &Block{} type Program struct { *Block } func MakeProgram() *Program { b := &Program{MakeBlock()} b.SetLabel("program") return b } func ProgramParse(stream *PeekTokenStream) ASTNode { p := MakeProgram() for stmt := StmtParse(stream); nil != stmt; { p.AddChild(stmt) stmt = StmtParse(stream) } return p } ================================================ FILE: parser/ast/scalar.go ================================================ package ast var _ ASTNode = &Factor{} type Scalar struct { *Factor } func NewScalar(stream *PeekTokenStream) *Scalar { return &Scalar{NewFactor(stream)} } func MakeScalar() *Scalar { s := &Scalar{MakeFactor()} s.SetType(ASTNODE_TYPE_SCALAR) return s } ================================================ FILE: parser/ast/stmt.go ================================================ package ast var DefaultStmt ASTNode = MakeStmt() type Stmt struct { *node } func MakeStmt() *Stmt { s := &Stmt{MakeNode()} return s } func StmtParse(stream *PeekTokenStream) ASTNode { if !stream.HasNext(){ return nil } token := stream.Next() lookahead := stream.Peek() stream.PutBack(1) if token.IsVariable() && lookahead != nil && lookahead.Value == "=" { return AssignStmtParse(stream) } else if token.Value == "var" { return DeclareStmtParse(stream) } else if token.Value == "func" { return FuncDeclareStmtParse(stream) } else if token.Value == "return" { return ReturnStmtParse(stream) } else if token.Value == "if" { return IfStmtParse(stream) } else if token.Value == "{" { return BlockParse(stream) } else { return ExprParse(stream) } } ================================================ FILE: parser/ast/stmt_assign.go ================================================ package ast var DefaultAssignStmt ASTNode = MakeAssignStmt() type AssignStmt struct { *Stmt } func MakeAssignStmt() *AssignStmt { v := &AssignStmt{MakeStmt()} v.SetType(ASTNODE_TYPE_ASSIGN_STMT) v.SetLabel("assign_stmt") return v } func AssignStmtParse(stream *PeekTokenStream) ASTNode { stmt := MakeAssignStmt() //stmt.SetParent(parent) tkn := stream.Peek() factor := FactorParse(stream) if nil == factor { panic("syntax error:" + tkn.String()) } stmt.AddChild(factor) lexeme := stream.NextMatch("=") stmt.SetLexeme(lexeme) expr := ExprParse(stream) stmt.AddChild(expr) return stmt } ================================================ FILE: parser/ast/stmt_assign_test.go ================================================ package ast import ( "bytes" "github.com/magiconair/properties/assert" "testing" "tinyscript/lexer" ) func TestAssignStmtParse(t *testing.T) { src := "i = 100*2" tokens := lexer.NewLexer(bytes.NewBufferString(src), lexer.EndToken).Analyse() stream := NewPeekTokenStream(tokens) stmt := AssignStmtParse(stream) assert.Equal(t, ToPostfixExpr(stmt), "i 100 2 * =") } ================================================ FILE: parser/ast/stmt_declare.go ================================================ package ast var DefaultDeclareStmt ASTNode = MakeDeclareStmt() type DeclareStmt struct { *Stmt } func NewDeclareStmt() *DeclareStmt { d := MakeDeclareStmt() return d } func MakeDeclareStmt() *DeclareStmt { v := &DeclareStmt{MakeStmt()} v.SetType(ASTNODE_TYPE_DECLARE_STMT) v.SetLabel("declare_stmt") return v } func DeclareStmtParse(stream *PeekTokenStream) ASTNode { stmt := NewDeclareStmt() stream.NextMatch("var") tkn := stream.Peek() factor := FactorParse(stream) if nil == factor { panic("syntax error:" + tkn.String()) } stmt.AddChild(factor) lexeme := stream.NextMatch("=") stmt.SetLexeme(lexeme) expr := ExprParse(stream) stmt.AddChild(expr) return stmt } ================================================ FILE: parser/ast/stmt_declare_test.go ================================================ package ast import ( "bytes" "github.com/magiconair/properties/assert" "testing" "tinyscript/lexer" ) func TestDeclareStmtParse(t *testing.T) { src := "var i = 100*2" tokens := lexer.NewLexer(bytes.NewBufferString(src), lexer.EndToken).Analyse() stream := NewPeekTokenStream(tokens) stmt := DeclareStmtParse(stream) assert.Equal(t, ToPostfixExpr(stmt), "i 100 2 * =") } ================================================ FILE: parser/ast/stmt_for.go ================================================ package ast var _ ASTNode = MakeForStmt() type ForStmt struct { *Stmt } func MakeForStmt() *ForStmt { v := &ForStmt{MakeStmt()} v.SetType(ASTNODE_TYPE_FOR_STMT) v.SetLabel("for") return v } ================================================ FILE: parser/ast/stmt_func_declare.go ================================================ package ast var _ ASTNode = MakeFuncDeclareStmt() type FuncDeclareStmt struct { *Stmt } func MakeFuncDeclareStmt() *FuncDeclareStmt { v := &FuncDeclareStmt{MakeStmt()} v.SetType(ASTNODE_TYPE_FUNCTION_DECLARE_STMT) v.SetLabel("func") return v } func FuncDeclareStmtParse(stream *PeekTokenStream) *FuncDeclareStmt { stream.NextMatch("func") //func add() int {} fn := MakeFuncDeclareStmt() lexeme := stream.Peek() fnV := FactorParse(stream) //函数名ast节点 fn.SetLexeme(lexeme) //函数名token作为这个ast节点的lexeme fn.AddChild(fnV) stream.NextMatch("(") args := FuncArgsParse(stream) stream.NextMatch(")") fn.AddChild(args) keyword := stream.Next() if !keyword.IsType() { panic("syntax error: unexpected " + keyword.Value) } fnV.SetTypeLexeme(keyword) //函数名ast节点的类型,即:函数返回值类型,用token表示 block := BlockParse(stream) fn.AddChild(block) return fn } func (f *FuncDeclareStmt) FuncVariable() ASTNode { return f.GetChild(0) } func (f *FuncDeclareStmt) Args() ASTNode { return f.GetChild(1) } func (f *FuncDeclareStmt) FuncType() string { return f.FuncVariable().TypeLexeme().Value } func (f *FuncDeclareStmt) Block() ASTNode { return f.GetChild(2) } ================================================ FILE: parser/ast/stmt_func_declare_test.go ================================================ package ast import ( "github.com/magiconair/properties/assert" "testing" "tinyscript/lexer" ) func TestFuncDeclareStmtParse(t *testing.T) { stream := NewPeekTokenStream(lexer.FromFile("./../../tests/function.ts")) stmt := StmtParse(stream).(*FuncDeclareStmt) args := stmt.Args() assert.Equal(t, args.GetChild(0).Lexeme().Value, "a") assert.Equal(t, args.GetChild(1).Lexeme().Value, "b") typ := stmt.FuncType() assert.Equal(t, typ, "int") funcVariable := stmt.FuncVariable() assert.Equal(t, funcVariable.Lexeme().Value, "add") block := stmt.Block() assert.Equal(t, block.GetChild(0).Lexeme().Value, "return") } func TestFunctionRecursion(t *testing.T) { stream := NewPeekTokenStream(lexer.FromFile("./../../tests/recursion.ts")) stmt := StmtParse(stream).(*FuncDeclareStmt) assert.Equal(t, ToBFSString(stmt, 4), "func fact args block") assert.Equal(t, ToBFSString(stmt.Args(), 2), "args n") assert.Equal(t, ToBFSString(stmt.Block(), 3), "block if return") } ================================================ FILE: parser/ast/stmt_if.go ================================================ package ast var _ ASTNode = MakeIfStmt() type IfStmt struct { *Stmt } func MakeIfStmt() *IfStmt { v := &IfStmt{MakeStmt()} v.SetType(ASTNODE_TYPE_IF_STMT) v.SetLabel("if") return v } func IfStmtParse(stream *PeekTokenStream) ASTNode { return IfParse(stream) } //IfStmt -> If(Expr) Block Tail func IfParse(stream *PeekTokenStream) ASTNode { lexeme := stream.NextMatch("if") stream.NextMatch("(") ifStmt := MakeIfStmt() ifStmt.SetLexeme(lexeme) e := ExprParse(stream) ifStmt.AddChild(e) stream.NextMatch(")") block := BlockParse(stream) ifStmt.AddChild(block) tail := TailParse(stream) if tail != nil { ifStmt.AddChild(tail) } return ifStmt } //Tail -> else {Block} | else IfStmt | ⍷ func TailParse(stream *PeekTokenStream) ASTNode { if !stream.HasNext() || stream.Peek().Value != "else" { return nil } stream.NextMatch("else") lookahead := stream.Peek() if lookahead.Value == "{" { return BlockParse(stream) } else if lookahead.Value == "if" { return IfParse(stream) } return nil } func (i *IfStmt) GetExpr() ASTNode { return i.GetChild(0) } func (i *IfStmt) GetBlock() ASTNode { return i.GetChild(1) } func (i *IfStmt) GetElseBlock() ASTNode { block := i.GetChild(2) if block != nil && block.Type() == ASTNODE_TYPE_BLOCK { return block } return nil } func (i *IfStmt) GetElseIfStmt() ASTNode { ifStmt := i.GetChild(2) if ifStmt != nil && ifStmt.Type() == ASTNODE_TYPE_IF_STMT { return ifStmt } return nil } ================================================ FILE: parser/ast/stmt_if_test.go ================================================ package ast import ( "bytes" "github.com/magiconair/properties/assert" "testing" "tinyscript/lexer" ) func TestIfStmtParse(t *testing.T) { stream := createTokenStream(`if(a){ a = 1 }`) stmt := IfStmtParse(stream) e := stmt.GetChild(0) block := stmt.GetChild(1) assignStmt := block.GetChild(0) assert.Equal(t, e.Lexeme().Value, "a") assert.Equal(t, assignStmt.Lexeme().Value, "=") } func createTokenStream(src string) *PeekTokenStream { tokens := lexer.NewLexer(bytes.NewBufferString(src), lexer.EndToken).Analyse() stream := NewPeekTokenStream(tokens) return stream } func TestIfElseStmtParse(t *testing.T) { stream := createTokenStream(`if(a){ a = 1 }else{ a = 2 a = a * 3 }`) stmt := IfStmtParse(stream) expr := stmt.GetChild(0) block := stmt.GetChild(1) assignStmt := block.GetChild(0) elseBlock := stmt.GetChild(2) assignStmt2 := elseBlock.GetChild(0) assert.Equal(t, expr.Lexeme().Value, "a") assert.Equal(t, assignStmt.Lexeme().Value, "=") assert.Equal(t, assignStmt2.Lexeme().Value, "=") assert.Equal(t, len(elseBlock.Children()), 2) } ================================================ FILE: parser/ast/stmt_return.go ================================================ package ast var _ ASTNode = &ReturnStmt{} type ReturnStmt struct { *Stmt } func MakeReturnStmt() *ReturnStmt { v := &ReturnStmt{MakeStmt()} v.SetType(ASTNODE_TYPE_RETURN_STMT) v.SetLabel("return") return v } func ReturnStmtParse(stream *PeekTokenStream) ASTNode { var lexeme = stream.NextMatch("return") var expr = ExprParse(stream) var stmt = MakeReturnStmt() stmt.SetLexeme(lexeme) if expr != nil { stmt.AddChild(expr) } return stmt } ================================================ FILE: parser/ast/stream.go ================================================ package ast import ( "fmt" "tinyscript/lexer" ) type PeekTokenStream struct { tokens []*lexer.Token //TODO 保存lexer生成的tokens,更加好的方式不是全部存储,这样内存消耗会比较大 current int } func NewPeekTokenStream(tokens []*lexer.Token) *PeekTokenStream { return &PeekTokenStream{tokens: tokens} } func (pt *PeekTokenStream) Next() *lexer.Token { if pt.current >= len(pt.tokens) { return nil } t := pt.tokens[pt.current] pt.current++ return t } func (pt *PeekTokenStream) HasNext() bool { if pt.current >= len(pt.tokens) { return false } return true } func (pt *PeekTokenStream) Peek() *lexer.Token { t := pt.Next() if nil == t { return nil } pt.current -= 1 return t } //参数:n 表示退回多少个token func (pt *PeekTokenStream) PutBack(n int) { if pt.current-n < 0 { panic("putback parameter is invalid") } pt.current -= n //必须+1,因为初始化时current就指向第一个元素 } //下一个token的value匹配实参字符的话,返回这个token,否则panic func (pt *PeekTokenStream) NextMatch(value string) *lexer.Token { token := pt.Next() if token.Value != value { panic(fmt.Sprintf("syntax err: want value:%s,got %s", value, token.Value)) } return token } //下一个token匹配实参类型的话,返回下一个token func (pt *PeekTokenStream) NextMatchType(typ lexer.TokenType) *lexer.Token { token := pt.Next() if token.Typ != typ { panic(fmt.Sprintf("syntax err: want type: %s,got %s", typ, token.Value)) } return token } ================================================ FILE: parser/ast/stream_test.go ================================================ package ast import ( "bytes" "github.com/magiconair/properties/assert" "testing" lexer2 "tinyscript/lexer" ) func TestNewPeekTokenStream(t *testing.T) { tokens := lexer2.NewLexer(bytes.NewBufferString("a+b*c"), lexer2.EndToken).Analyse() peekts := NewPeekTokenStream(tokens) assert.Equal(t, peekts.HasNext(), true) assertToken(t, peekts.Next(), "a", lexer2.VARIABLE) assertToken(t, peekts.Next(), "+", lexer2.OPERATOR) assertToken(t, peekts.Peek(), "b", lexer2.VARIABLE) assertToken(t, peekts.Next(), "b", lexer2.VARIABLE) peekts.PutBack(3) assertToken(t, peekts.Peek(), "a", lexer2.VARIABLE) assertToken(t, peekts.Next(), "a", lexer2.VARIABLE) } func assertToken(t *testing.T, token *lexer2.Token, wantValue string, wantType lexer2.TokenType) { assert.Equal(t, token.Typ, wantType, "err detail:"+token.String()) assert.Equal(t, token.Value, wantValue, "err detail:"+token.String()) } ================================================ FILE: parser/ast/type.go ================================================ package ast type NodeType int const ( ASTNODE_TYPE_BLOCK NodeType = iota ASTNODE_TYPE_BINARY_EXPR // 1+1 ASTNODE_TYPE_UNARY_EXPR //++1 ASTNODE_TYPE_CALL_EXPR ASTNODE_TYPE_VARIABLE ASTNODE_TYPE_SCALAR // 1.0 true ASTNODE_TYPE_IF_STMT ASTNODE_TYPE_WHILE_STMT ASTNODE_TYPE_FOR_STMT ASTNODE_TYPE_RETURN_STMT ASTNODE_TYPE_ASSIGN_STMT ASTNODE_TYPE_FUNCTION_DECLARE_STMT ASTNODE_TYPE_DECLARE_STMT ) var NodeTypeStringMap = map[NodeType]string{ ASTNODE_TYPE_BLOCK: "block", ASTNODE_TYPE_ASSIGN_STMT: "assign_stmt", ASTNODE_TYPE_BINARY_EXPR: "binary_expr", ASTNODE_TYPE_UNARY_EXPR: "unary_expr", ASTNODE_TYPE_CALL_EXPR: "call_expr", ASTNODE_TYPE_DECLARE_STMT: "declare_stmt", ASTNODE_TYPE_FOR_STMT: "for_stmt", ASTNODE_TYPE_FUNCTION_DECLARE_STMT: "function_declare_stmt", ASTNODE_TYPE_IF_STMT: "if_stmt", ASTNODE_TYPE_RETURN_STMT: "return_stmt", ASTNODE_TYPE_SCALAR: "scalar", ASTNODE_TYPE_VARIABLE: "variable", ASTNODE_TYPE_WHILE_STMT: "while_stmt", } func (nt NodeType) String() string { return NodeTypeStringMap[nt] } ================================================ FILE: parser/ast/util.go ================================================ package ast import ( "container/list" "strings" ) func ToPostfixExpr(node ASTNode) string { if node.Type() == ASTNODE_TYPE_SCALAR || node.Type() == ASTNODE_TYPE_VARIABLE { return node.Lexeme().Value } arr := []string{} for _, child := range node.Children() { arr = append(arr, ToPostfixExpr(child)) } str := "" if nil != node.Lexeme() { str = node.Lexeme().Value } if len(str) > 0 { return strings.Join(arr, " ") + " " + str } return strings.Join(arr, " ") //left := "" //right := "" // //switch node.Type() { //case ASTNODE_TYPE_BINARY_EXPR: // left = ToPostfixExpr(node.GetChild(0)) // right = ToPostfixExpr(node.GetChild(1)) // return left + " " + right + " " + node.Lexeme().Value //case ASTNODE_TYPE_SCALAR, ASTNODE_TYPE_VARIABLE: // return node.Lexeme().Value //} // //panic("ToPostfixExpr failed") } func ToBFSString(node ASTNode, max int) string { l := list.New() l.PushBack(node) strs := []string{} for e, i := l.Front(), 0; nil != e && i < max; e = l.Front() { i += 1 parent := l.Remove(e).(ASTNode) strs = append(strs, parent.Label()) for _, child := range parent.Children() { l.PushBack(child) } } return strings.Join(strs, " ") } ================================================ FILE: parser/ast/variable.go ================================================ package ast var _ ASTNode = &Variable{} type Variable struct { *Factor } func NewVariable(stream *PeekTokenStream) *Variable { return &Variable{NewFactor(stream)} } func MakeVariable() *Variable { v := &Variable{MakeFactor()} v.SetType(ASTNODE_TYPE_VARIABLE) return v } ================================================ FILE: parser/parser.go ================================================ package parser import ( "tinyscript/lexer" "tinyscript/parser/ast" ) type Parser struct { stream *ast.PeekTokenStream } func Parse(source string) ast.ASTNode { return NewParser(lexer.Analyse(source)).parse() } func ParseFromFile(file string) ast.ASTNode { tokens := lexer.FromFile(file) return NewParser(tokens).parse() } func NewParser(tokens []*lexer.Token) *Parser { return &Parser{stream: ast.NewPeekTokenStream(tokens)} } func (p *Parser) parse() ast.ASTNode { return ast.ProgramParse(p.stream) } //Expr -> digit + Expr | d|igit //digit -> 0|1|2|....|9 func (p *Parser) SimpleParse() ast.ASTNode { expr := ast.MakeExpr() scalar := ast.NewScalar(p.stream) if !p.stream.HasNext() { return scalar } expr.SetLexeme(p.stream.Peek()) p.stream.NextMatch("+") expr.SetLabel("+") expr.SetType(ast.ASTNODE_TYPE_BINARY_EXPR) expr.AddChild(scalar) rightNode := p.SimpleParse() expr.AddChild(rightNode) return expr } ================================================ FILE: parser/parser_test.go ================================================ package parser import ( "bytes" "github.com/magiconair/properties/assert" "testing" lexer "tinyscript/lexer" "tinyscript/parser/ast" ) func TestParser_Parse(t *testing.T) { source := "1+2+3+4" parser := NewParser(lexer.NewLexer(bytes.NewBufferString(source), lexer.EndToken).Analyse()) expr := parser.SimpleParse() assert.Equal(t, len(expr.Children()), 2) v1 := expr.GetChild(0) assert.Equal(t, v1.Lexeme().Value, "1") assert.Equal(t, expr.Lexeme().Value, "+") e2 := expr.GetChild(1) v2 := e2.GetChild(0) assert.Equal(t, v2.Lexeme().Value, "2") assert.Equal(t, e2.Lexeme().Value, "+") e3 := e2.GetChild(1) v3 := e3.GetChild(0) assert.Equal(t, v3.Lexeme().Value, "3") assert.Equal(t, e3.Lexeme().Value, "+") v4 := e3.GetChild(1) assert.Equal(t, v4.Lexeme().Value, "4") } func createExpr(src string) ast.ASTNode { tokens := lexer.NewLexer(bytes.NewBufferString(src), lexer.EndToken).Analyse() stream := ast.NewPeekTokenStream(tokens) return ast.ExprParse(stream) } func TestSimple(t *testing.T) { expr := createExpr("1+1+1") assert.Equal(t, ast.ToPostfixExpr(expr), "1 1 1 + +") } func TestSimple1(t *testing.T) { expr := createExpr(`"123" == ""`) assert.Equal(t, ast.ToPostfixExpr(expr), `"123" "" ==`) } func TestComplex(t *testing.T) { expr1 := createExpr("1+2*3") expr2 := createExpr("1*2+3") e3 := createExpr("10 * (7+4)") e4 := createExpr("(1*2!=7)==3!=4*5+6") assert.Equal(t, ast.ToPostfixExpr(expr1), "1 2 3 * +") assert.Equal(t, ast.ToPostfixExpr(expr2), "1 2 * 3 +") assert.Equal(t, ast.ToPostfixExpr(e3), "10 7 4 + *") assert.Equal(t, ast.ToPostfixExpr(e4), "1 2 * 7 != 3 4 5 * 6 + != ==") } ================================================ FILE: tests/add.ts ================================================ func add(int a, int b) int { return a + b } func main() void { add(10, 20) return } ================================================ FILE: tests/complex-if.ts ================================================ if(a == 1) { b = 100 } else if(a == 2) { b = 500 } else if(a == 3) { b = a * 1000 } else { b = -1 } ================================================ FILE: tests/fact2.ts ================================================ func fact(int n) int { if(n == 0) { return 1 } return fact(n-1) * n } func main() void { return fact(2) } ================================================ FILE: tests/fact5.ts ================================================ func fact(int n) int { if(n == 0) { return 1 } return fact(n-1) * n } func main() void { return fact(5) } ================================================ FILE: tests/function.ts ================================================ func add(int a,int b)int{ return a + b } ================================================ FILE: tests/recursion.ts ================================================ func fact(int n)int{ if (n ==0){ return 1 } return fact(n-1)*n } ================================================ FILE: translator/static_table_test.go ================================================ package translator import ( "github.com/magiconair/properties/assert" "testing" "tinyscript/parser" ) func TestStaticTable(t *testing.T) { source := `if(a){a=1}else{b=a+1*5}` node := parser.Parse(source) program := NewTranslator().Translate(node) assert.Equal(t, program.StaticTable.Size(), 2) } ================================================ FILE: translator/symbol/static_table.go ================================================ package symbol import ( "fmt" "strings" ) type StaticSymbolTable struct { OffsetMap map[string]*Symbol OffsetCounter int Symbols []*Symbol } func NewStaticSymbolTable() *StaticSymbolTable { return &StaticSymbolTable{OffsetCounter: 0, OffsetMap: make(map[string]*Symbol), Symbols: make([]*Symbol, 0)} } func (s *StaticSymbolTable) Add(symbol *Symbol) { lexval := symbol.Lexeme.Value if _, ok := s.OffsetMap[lexval]; !ok { s.OffsetMap[lexval] = symbol symbol.Offset = s.OffsetCounter s.OffsetCounter += 1 s.Symbols = append(s.Symbols, symbol) } else { sameSymbol := s.OffsetMap[lexval] symbol.Offset = sameSymbol.Offset } } func (s *StaticSymbolTable) Size() int { return len(s.Symbols) } func (s *StaticSymbolTable) String() string { var list []string for i, v := range s.Symbols { list = append(list, fmt.Sprintf("%d:%s", i, v)) } return strings.Join(list, "\n") } ================================================ FILE: translator/symbol/symbol.go ================================================ package symbol import "tinyscript/lexer" type Symbol struct { Parent *Table Lexeme *lexer.Token Label string Offset int LayerOffset int Typ SymbolType } func NewSymbol(typ SymbolType) *Symbol { return &Symbol{Typ: typ} } func (s *Symbol) String() string { if SYMBOL_LABEL == s.Typ { return s.Label } return s.Lexeme.Value } func MakeAddressSymbol(lexeme *lexer.Token, offset int) *Symbol { syb := NewSymbol(SYMBOL_ADDRESS) syb.Lexeme = lexeme syb.Offset = offset return syb } func MakeImmediateSymbol(lexeme *lexer.Token) *Symbol { syb := NewSymbol(SYMBOL_IMMEDIATE) syb.Lexeme = lexeme return syb } func MakeLabelSymbol(label string, lexeme *lexer.Token) *Symbol { syb := NewSymbol(SYMBOL_LABEL) syb.Lexeme = lexeme syb.Label = label return syb } ================================================ FILE: translator/symbol/table.go ================================================ package symbol import ( "fmt" "tinyscript/lexer" ) /* 一个符号表在运行时就是活动记录,一个符号表可以对应多个活动记录(递归),符号表这个时候就是一个模板 */ type Table struct { Parent *Table Children []*Table Symbols []*Symbol TempIndex int OffsetIndex int Level int } func NewTable() *Table { return &Table{ Symbols: make([]*Symbol, 0), Children: make([]*Table, 0), } } func (t *Table) AddSymbol(symbol *Symbol) { t.Symbols = append(t.Symbols, symbol) symbol.Parent = t } func (t *Table) symbolByLexeme(lexeme *lexer.Token) *Symbol { for _, v := range t.Symbols { if lexeme.Value == v.Lexeme.Value { return v } } return nil } func (t *Table) Exists(lexeme *lexer.Token) bool { symbol := t.symbolByLexeme(lexeme) if nil != symbol { return true } if t.Parent != nil { return t.Parent.Exists(lexeme) } return false } func (t *Table) CloneFromSymbolTree(lexeme *lexer.Token, layoutOffset int) *Symbol { symbl := t.symbolByLexeme(lexeme) if nil != symbl { symbol := *symbl symbol.LayerOffset = layoutOffset return &symbol } if nil != t.Parent { return t.Parent.CloneFromSymbolTree(lexeme, layoutOffset+1) } return nil } func (t *Table) CreateSymbolByLexeme(lexeme *lexer.Token) *Symbol { var symbol *Symbol = nil if lexeme.IsScalar() { symbol = MakeImmediateSymbol(lexeme) t.AddSymbol(symbol) } else { symbol2 := t.symbolByLexeme(lexeme) if nil == symbol2 { symbol = t.CloneFromSymbolTree(lexeme, 0) if symbol == nil { symbol = MakeAddressSymbol(lexeme, t.OffsetIndex) t.OffsetIndex += 1 } t.AddSymbol(symbol) } else { symbol = symbol2 } } return symbol } func (t *Table) CreateVariable() *Symbol { lexeme := lexer.NewToken(lexer.VARIABLE, "p"+fmt.Sprintf("%d", t.TempIndex)) t.TempIndex += 1 symbol := MakeAddressSymbol(lexeme, t.OffsetIndex) t.OffsetIndex += 1 t.AddSymbol(symbol) return symbol } func (t *Table) AddChild(child *Table) { child.Parent = t child.Level = t.Level + 1 t.Children = append(t.Children, child) } func (t *Table) LocalSize() int { return t.OffsetIndex } func (t *Table) CreateLabel(label string, lexeme *lexer.Token) { labelSymbol := MakeLabelSymbol(label, lexeme) t.AddSymbol(labelSymbol) } ================================================ FILE: translator/symbol/table_test.go ================================================ package symbol import ( "github.com/magiconair/properties/assert" "testing" "tinyscript/lexer" ) func TestSymbolTable(t *testing.T) { table := NewTable() table.CreateLabel("L0", lexer.NewToken(lexer.VARIABLE, "foo")) table.CreateVariable() table.CreateSymbolByLexeme(lexer.NewToken(lexer.VARIABLE, "foo")) assert.Equal(t, table.LocalSize(), 1) } func TestTableChain(t *testing.T) { table := NewTable() table.CreateSymbolByLexeme(lexer.NewToken(lexer.VARIABLE, "a")) childTable := NewTable() table.AddChild(childTable) childChildTable := NewTable() childTable.AddChild(childChildTable) assert.Equal(t, childChildTable.Exists(lexer.NewToken(lexer.VARIABLE, "a")), true) assert.Equal(t, childTable.Exists(lexer.NewToken(lexer.VARIABLE, "a")), true) } func TestOffset(t *testing.T) { table := NewTable() table.CreateSymbolByLexeme(lexer.NewToken(lexer.INTEGER, "100")) symbola := table.CreateSymbolByLexeme(lexer.NewToken(lexer.VARIABLE, "a")) symbolb := table.CreateSymbolByLexeme(lexer.NewToken(lexer.VARIABLE, "b")) childTable := NewTable() table.AddChild(childTable) anotherSymbolB := childTable.CreateSymbolByLexeme(lexer.NewToken(lexer.VARIABLE, "b")) symbolC := childTable.CreateSymbolByLexeme(lexer.NewToken(lexer.VARIABLE, "c")) assert.Equal(t, symbola.Offset, 0) assert.Equal(t, symbolb.Offset, 1) assert.Equal(t, anotherSymbolB.Offset, 1) assert.Equal(t, anotherSymbolB.LayerOffset, 1) assert.Equal(t, symbolC.Offset, 0) assert.Equal(t, symbolC.LayerOffset, 0) } ================================================ FILE: translator/symbol/types.go ================================================ package symbol type SymbolType int const ( SYMBOL_ADDRESS SymbolType = iota SYMBOL_IMMEDIATE SYMBOL_LABEL ) func (s SymbolType) String() string { switch s { case SYMBOL_ADDRESS: return "symbol_address" case SYMBOL_IMMEDIATE: return "symbol_immediate" case SYMBOL_LABEL: return "symbol_label" } panic("unknown symbol type") } ================================================ FILE: translator/symbol/util.go ================================================ package symbol ================================================ FILE: translator/tainstruction.go ================================================ package translator import ( "fmt" "tinyscript/translator/symbol" ) type TAInstruction struct { Arg1 interface{} Arg2 interface{} Op string Result *symbol.Symbol Typ TAInstructionType Label string } func NewTAInstruction(typ TAInstructionType, result *symbol.Symbol, op string, arg1 interface{}, arg2 interface{}) *TAInstruction { return &TAInstruction{Arg1: arg1, Arg2: arg2, Op: op, Result: result, Typ: typ} } func (t TAInstruction) String() string { switch t.Typ { case TAINSTR_TYPE_ASSIGN: if nil != t.Arg2 { return fmt.Sprintf("%v = %v %v %v", t.Result, t.Arg1, t.Op, t.Arg2) } else { return fmt.Sprintf("%v = %v", t.Result, t.Arg1) } case TAINSTR_TYPE_IF: return fmt.Sprintf("IF %v ELSE %v", t.Arg1, t.Arg2) case TAINSTR_TYPE_GOTO: return fmt.Sprintf("GOTO %v", t.Arg1) case TAINSTR_TYPE_LABEL: return fmt.Sprintf("%v:", t.Arg1) case TAINSTR_TYPE_FUNC_BEGIN: return "FUNC_BEGIN" case TAINSTR_TYPE_RETURN: if !IsNil(t.Arg1) { return fmt.Sprintf("RETURN %v", t.Arg1) } return fmt.Sprintf("RETURN") case TAINSTR_TYPE_PARAM: return fmt.Sprintf("PARAM %v %v", t.Arg1, t.Arg2) case TAINSTR_TYPE_SP: return fmt.Sprintf("SP %v", t.Arg1) case TAINSTR_TYPE_CALL: return fmt.Sprintf("CALL %v", t.Arg1) } panic("unknown opcode type") } ================================================ FILE: translator/tainstruction_type.go ================================================ package translator type TAInstructionType int const ( TAINSTR_TYPE_ASSIGN TAInstructionType = iota TAINSTR_TYPE_GOTO TAINSTR_TYPE_IF TAINSTR_TYPE_LABEL TAINSTR_TYPE_CALL TAINSTR_TYPE_RETURN TAINSTR_TYPE_SP TAINSTR_TYPE_PARAM TAINSTR_TYPE_FUNC_BEGIN ) ================================================ FILE: translator/taprogram.go ================================================ package translator import ( "fmt" "strings" "tinyscript/translator/symbol" ) type TAProgram struct { Instructions []*TAInstruction LabelCounter int StaticTable *symbol.StaticSymbolTable } func NewTAProgram() *TAProgram { return &TAProgram{Instructions: make([]*TAInstruction, 0), StaticTable: symbol.NewStaticSymbolTable()} } func (t *TAProgram) Add(instr *TAInstruction) { t.Instructions = append(t.Instructions, instr) } func (t *TAProgram) AddLabel() *TAInstruction { label := fmt.Sprintf("L%d", t.LabelCounter) t.LabelCounter += 1 taCode := NewTAInstruction(TAINSTR_TYPE_LABEL, nil, "", nil, nil) taCode.Arg1 = label t.Instructions = append(t.Instructions, taCode) return taCode } func (t *TAProgram) String() string { var lines []string for _, v := range t.Instructions { lines = append(lines, v.String()) } return strings.Join(lines, "\n") } //根据符号表的内容,判断符号类型,如果是SYMBOL_IMMEDIATE,则加入静态符号表,以此来设置静态符号表的信息 func (t *TAProgram) SetStaticSymbols(table *symbol.Table) { for _, v := range table.Symbols { if symbol.SYMBOL_IMMEDIATE == v.Typ { t.StaticTable.Add(v) } } for _, child := range table.Children { t.SetStaticSymbols(child) } } ================================================ FILE: translator/translator.go ================================================ package translator import ( "fmt" "tinyscript/lexer" "tinyscript/parser/ast" "tinyscript/translator/symbol" ) type Translator struct { } func NewTranslator() *Translator { return &Translator{} } /* 符号表是辅助工具。对ast遍历的同时产生符号表,根据符号表的内容产生TAProgram。 */ func (t *Translator) Translate(node ast.ASTNode) *TAProgram { program := NewTAProgram() table := symbol.NewTable() for _, child := range node.Children() { t.TranslateStmt(program, child, table) } program.SetStaticSymbols(table) mainFn := lexer.NewToken(lexer.VARIABLE, "main") if table.Exists(mainFn) { table.CreateVariable() //返回值 program.Add(NewTAInstruction(TAINSTR_TYPE_SP, nil, "", -table.LocalSize(), nil)) program.Add(NewTAInstruction(TAINSTR_TYPE_CALL, nil, "", table.CloneFromSymbolTree(mainFn, 0), nil)) program.Add(NewTAInstruction(TAINSTR_TYPE_SP, nil, "", table.LocalSize(), nil)) } return program } func (t *Translator) TranslateStmt(program *TAProgram, node ast.ASTNode, table *symbol.Table) { switch node.Type() { case ast.ASTNODE_TYPE_BLOCK: t.TranslateBlock(program, node, table) return case ast.ASTNODE_TYPE_IF_STMT: t.TranslateIfStmt(program, node.(*ast.IfStmt), table) return case ast.ASTNODE_TYPE_ASSIGN_STMT: t.TranslateAssignStmt(program, node, table) return case ast.ASTNODE_TYPE_DECLARE_STMT: t.TranslateDeclareStmt(program, node, table) return case ast.ASTNODE_TYPE_FUNCTION_DECLARE_STMT: t.TranslateFunctionDeclareStmt(program, node, table) return case ast.ASTNODE_TYPE_RETURN_STMT: t.TranslateReturnStmt(program, node, table) return case ast.ASTNODE_TYPE_CALL_EXPR: t.TranslateCallExpr(program, node, table) return } panic("unknown node type" + node.Type().String()) } func (t *Translator) TranslateDeclareStmt(program *TAProgram, node ast.ASTNode, table *symbol.Table) { lexeme := node.GetChild(0).Lexeme() if table.Exists(lexeme) { panic("Syntax Error, Identifier " + lexeme.Value + " is already defined") } assigned := table.CreateSymbolByLexeme(lexeme) expr := node.GetChild(1) addr := t.TranslateExpr(program, expr, table) program.Add(NewTAInstruction(TAINSTR_TYPE_ASSIGN, assigned, "=", addr, nil)) } func (t *Translator) TranslateAssignStmt(program *TAProgram, node ast.ASTNode, table *symbol.Table) { assigned := table.CreateSymbolByLexeme(node.GetChild(0).Lexeme()) expr := node.GetChild(1) addr := t.TranslateExpr(program, expr, table) program.Add(NewTAInstruction(TAINSTR_TYPE_ASSIGN, assigned, "=", addr, nil)) } /* SDD: E -> E1 op E2 E -> F */ func (t *Translator) TranslateExpr(program *TAProgram, node ast.ASTNode, table *symbol.Table) *symbol.Symbol { if node.IsValueType() { addr := table.CreateSymbolByLexeme(node.Lexeme()) node.SetProp("addr", addr) return addr } else if node.Type() == ast.ASTNODE_TYPE_CALL_EXPR { addr := t.TranslateCallExpr(program, node, table) node.SetProp("addr", addr) return addr } else if IsInstanceOfExpr(node) { for _, child := range node.Children() { t.TranslateExpr(program, child, table) } if node.Prop("addr") == nil { node.SetProp("addr", table.CreateVariable()) } instr := NewTAInstruction( TAINSTR_TYPE_ASSIGN, node.Prop("addr").(*symbol.Symbol), node.Lexeme().Value, node.GetChild(0).Prop("addr").(*symbol.Symbol), node.GetChild(1).Prop("addr").(*symbol.Symbol), ) program.Add(instr) return instr.Result } panic("unexpected node type :" + node.Type().String()) } func (t *Translator) TranslateBlock(program *TAProgram, node ast.ASTNode, parent *symbol.Table) { table := symbol.NewTable() parent.AddChild(table) parentOffset := table.CreateVariable() parentOffset.Lexeme = lexer.NewToken(lexer.INTEGER, fmt.Sprintf("%d", parent.LocalSize())) //pushRecord := NewTAInstruction(TAINSTR_TYPE_SP, nil, "", nil, nil) //program.Add(pushRecord) for _, stmt := range node.Children() { t.TranslateStmt(program, stmt, table) } //popRecord := NewTAInstruction(TAINSTR_TYPE_SP, nil, "", nil, nil) //program.Add(popRecord) // //pushRecord.Arg1 = -parent.LocalSize() //popRecord.Arg1 = parent.LocalSize() } func (t *Translator) TranslateIfStmt(program *TAProgram, node *ast.IfStmt, table *symbol.Table) { expr := node.GetExpr() exprAddr := t.TranslateExpr(program, expr, table) ifOpCode := NewTAInstruction(TAINSTR_TYPE_IF, nil, "", exprAddr, nil) program.Add(ifOpCode) t.TranslateBlock(program, node.GetBlock(), table) var gotoInstr *TAInstruction = nil if node.GetChild(2) != nil { gotoInstr = NewTAInstruction(TAINSTR_TYPE_GOTO, nil, "", nil, nil) program.Add(gotoInstr) labelEndIf := program.AddLabel() ifOpCode.Arg2 = labelEndIf.Arg1 } if node.GetElseBlock() != nil { t.TranslateBlock(program, node.GetElseBlock(), table) } else if node.GetElseIfStmt() != nil { t.TranslateIfStmt(program, node.GetElseIfStmt().(*ast.IfStmt), table) } labelEnd := program.AddLabel() if node.GetChild(2) == nil { ifOpCode.Arg2 = labelEnd.Arg1 } else { gotoInstr.Arg1 = labelEnd.Arg1 } } func (t *Translator) TranslateFunctionDeclareStmt(program *TAProgram, node ast.ASTNode, parent *symbol.Table) { label := program.AddLabel() table := symbol.NewTable() program.Add(NewTAInstruction(TAINSTR_TYPE_FUNC_BEGIN, nil, "", nil, nil)) table.CreateVariable() //返回地址 label.Arg2 = node.Lexeme().Value fn := node.(*ast.FuncDeclareStmt) args := fn.Args() parent.AddChild(table) parent.CreateLabel(label.Arg1.(string), node.Lexeme()) for _, arg := range args.Children() { table.CreateSymbolByLexeme(arg.Lexeme()) } for _, child := range fn.Block().Children() { t.TranslateStmt(program, child, table) } } func (t *Translator) TranslateCallExpr(program *TAProgram, node ast.ASTNode, table *symbol.Table) *symbol.Symbol { //foo() factor := node.GetChild(0) //foo -> symbol(foo) L0 //table.CreateVariable() //返回地址 var l = make([]*TAInstruction, 0) for i := 1; i < len(node.Children()); i++ { expr := node.GetChild(uint(i)) addr := t.TranslateExpr(program, expr, table) l = append(l, NewTAInstruction(TAINSTR_TYPE_PARAM, nil, "", addr, i-1)) } for _, instr := range l { instr.Arg2 = table.LocalSize() + instr.Arg2.(int) + 2 program.Add(instr) } returnValue := table.CreateVariable() //返回值 funcAddr := table.CloneFromSymbolTree(factor.Lexeme(), 0) if nil == funcAddr { panic("function " + factor.Lexeme().Value + " not found") } program.Add(NewTAInstruction(TAINSTR_TYPE_SP, nil, "", -table.LocalSize(), nil)) program.Add(NewTAInstruction(TAINSTR_TYPE_CALL, nil, "", funcAddr, nil)) program.Add(NewTAInstruction(TAINSTR_TYPE_SP, nil, "", table.LocalSize(), nil)) return returnValue } func (t *Translator) TranslateReturnStmt(program *TAProgram, node ast.ASTNode, table *symbol.Table) { var resultValue *symbol.Symbol = nil if node.GetChild(0) != nil { resultValue = t.TranslateExpr(program, node.GetChild(0), table) } program.Add(NewTAInstruction(TAINSTR_TYPE_RETURN, nil, "", resultValue, nil)) } ================================================ FILE: translator/translator_test.go ================================================ package translator import ( "github.com/magiconair/properties/assert" "testing" "tinyscript/parser" "tinyscript/translator/symbol" ) func TestExprTranslator(t *testing.T) { source := `a+(b-c)+d*(b-c)*2` p := parser.Parse(source) exprNode := p.GetChild(0) translator := NewTranslator() table := symbol.NewTable() program := NewTAProgram() translator.TranslateExpr(program, exprNode, table) expected := `p0 = b - c p1 = b - c p2 = p1 * 2 p3 = d * p2 p4 = p0 + p3 p5 = a + p4` assert.Equal(t, program.String(), expected) } func TestAssignStmt(t *testing.T) { source := "a=1.0*2.0*3.0" node := parser.Parse(source) translator := NewTranslator() program := translator.Translate(node) expected := `p0 = 2.0 * 3.0 p1 = 1.0 * p0 a = p1` assert.Equal(t, program.String(), expected) } func TestTranslator_TranslateDeclareStmt(t *testing.T) { source := "var a=1.0*2.0*3.0" node := parser.Parse(source) translator := NewTranslator() program := translator.Translate(node) expected := `p0 = 2.0 * 3.0 p1 = 1.0 * p0 a = p1` assert.Equal(t, program.String(), expected) } func TestAssignStmt2(t *testing.T) { source := "a=1" node := parser.Parse(source) translator := NewTranslator() program := translator.Translate(node) assert.Equal(t, program.String(), "a = 1") } func TestBlock(t *testing.T) { sourc := `var a = 1 { var b = 1 * 100 } { var b = a * 100 }` ast := parser.Parse(sourc) translator := NewTranslator() program := translator.Translate(ast) expected := `a = 1 p1 = 1 * 100 b = p1 p1 = a * 100 b = p1` assert.Equal(t, program.String(), expected) } func TestTranslator_TranslateIfStmt(t *testing.T) { source := `if(a){ b=1 }` astNode := parser.Parse(source) translator := NewTranslator() program := translator.Translate(astNode) expected := `IF a ELSE L0 b = 1 L0:` assert.Equal(t, program.String(), expected) } func TestTranslator_TranslateIfElseStmt(t *testing.T) { source := `if(a){ b=1 }else{ b=2 }` astNode := parser.Parse(source) translator := NewTranslator() program := translator.Translate(astNode) expected := `IF a ELSE L0 b = 1 GOTO L1 L0: b = 2 L1:` assert.Equal(t, program.String(), expected) } func TestTranslator_TranslateIfElseIfStmt(t *testing.T) { astNode := parser.ParseFromFile("../tests/complex-if.ts") translator := NewTranslator() program := translator.Translate(astNode) expected := `p0 = a == 1 IF p0 ELSE L0 b = 100 GOTO L5 L0: p1 = a == 2 IF p1 ELSE L1 b = 500 GOTO L4 L1: p2 = a == 3 IF p2 ELSE L2 p1 = a * 1000 b = p1 GOTO L3 L2: b = -1 L3: L4: L5:` assert.Equal(t, program.String(), expected) } func TestSimpleFunction(t *testing.T) { node := parser.ParseFromFile("../tests/function.ts") translator := NewTranslator() program := translator.Translate(node) expected := `L0: FUNC_BEGIN p1 = a + b RETURN p1` assert.Equal(t, program.String(), expected) } func TestRecursionFunc(t *testing.T) { node := parser.ParseFromFile("../tests/recursion.ts") translator := NewTranslator() program := translator.Translate(node) expected := `L0: FUNC_BEGIN p1 = n == 0 IF p1 ELSE L1 RETURN 1 L1: p2 = n - 1 PARAM p2 6 SP -5 CALL L0 SP 5 p4 = p3 * n RETURN p4` assert.Equal(t, program.String(), expected) } ================================================ FILE: translator/util.go ================================================ package translator import ( "reflect" "strings" ) func IsInstanceOfExpr(instance interface{}) bool { ival := reflect.ValueOf(instance) return strings.LastIndex(ival.Type().String(), "ast.Expr") != -1 } func IsNil(i interface{}) bool { vi := reflect.ValueOf(i) //if vi.Kind() == reflect.Ptr { return vi.IsNil() //} //return false } ================================================ FILE: translator/util_test.go ================================================ package translator import ( "github.com/magiconair/properties/assert" "testing" "tinyscript/parser/ast" ) func TestIsInstanceOf(t *testing.T) { var i interface{} i = &ast.Expr{} assert.Equal(t, IsInstanceOfExpr(i), true) } ================================================ FILE: vm/vm.go ================================================ package vm import ( "log" "tinyscript/gen" "tinyscript/gen/operand" ) type VM struct { Registers [31]int Memory [4096]int EndProgramSection int StartProgram int } func NewVM(staticArea []int, opcodes []int, entry *int) *VM { vm := &VM{} i := 0 for ; i < len(staticArea); i++ { vm.Memory[i] = staticArea[i] } j := i vm.StartProgram = i //mainStart := *entry + i for ; i < len(opcodes)+j; i++ { vm.Memory[i] = opcodes[i-j] } vm.Registers[operand.PC.Addr] = i - 3 vm.EndProgramSection = i vm.Registers[operand.SP.Addr] = 4095 return vm } func (vm *VM) Fetch() int { pc := vm.Registers[operand.PC.Addr] return vm.Memory[pc] } func (vm *VM) Decode(code int) *gen.Instruction { return gen.FromByCode(code) } func (vm *VM) Exec(instr *gen.Instruction) { code := instr.Code.Value log.Println("exec:", instr) switch code { case 0x01: //ADD r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.Register) r2 := instr.GetOperand(2).(*operand.Register) vm.Registers[r0.Addr] = vm.Registers[r1.Addr] + vm.Registers[r2.Addr] //case 0x09: // case 0x09, 0x02: //SUB r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.Register) r2 := instr.GetOperand(2).(*operand.Register) vm.Registers[r0.Addr] = vm.Registers[r1.Addr] - vm.Registers[r2.Addr] case 0x03: //MULT r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.Register) vm.Registers[operand.L0.Addr] = vm.Registers[r0.Addr] * vm.Registers[r1.Addr] case 0x05: //ADDI r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.ImmediateNumber) vm.Registers[r0.Addr] += r1.Value case 0x06: //SUBI r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.ImmediateNumber) vm.Registers[r0.Addr] -= r1.Value //case 0x07: //MULI case 0x08: //MFLO r0 := instr.GetOperand(0).(*operand.Register) vm.Registers[r0.Addr] = vm.Registers[operand.L0.Addr] case 0x10: //SW r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.Register) offset := instr.GetOperand(2).(*operand.Offset) R1VAL := vm.Registers[r1.Addr] vm.Memory[R1VAL+offset.Offset] = vm.Registers[r0.Addr] case 0x11: //LW r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.Register) offset := instr.GetOperand(2).(*operand.Offset) R1VAL := vm.Registers[r1.Addr] vm.Registers[r0.Addr] = vm.Memory[R1VAL+offset.Offset] case 0x15: //BNE r0 := instr.GetOperand(0).(*operand.Register) r1 := instr.GetOperand(1).(*operand.Register) offset := instr.GetOperand(2).(*operand.Offset) if vm.Registers[r0.Addr] != vm.Registers[r1.Addr] { vm.Registers[operand.PC.Addr] = offset.Offset + vm.StartProgram - 1 } case 0x20: //JUMP r0 := instr.GetOperand(0).(*operand.Offset) vm.Registers[operand.PC.Addr] = r0.Offset + vm.StartProgram - 1 case 0x21: //JR r0 := instr.GetOperand(0).(*operand.Offset) vm.Registers[operand.RA.Addr] = vm.Registers[operand.PC.Addr] vm.Registers[operand.PC.Addr] = r0.Offset + vm.StartProgram - 1 case 0x22: //RETRUN if instr.GetOperand(0) != nil { //match 返回值 } spVal := vm.Registers[operand.SP.Addr] vm.Registers[operand.PC.Addr] = vm.Memory[spVal] } } func (vm *VM) run() { //模拟CPU循环 // fetch // decode // exec // pc++ for ; vm.runOneStep(); { } } func (vm *VM) GetSpMemory(offset int) int { sp := vm.Registers[operand.SP.Addr] return vm.Memory[sp+offset] } func (vm *VM) runOneStep() bool { code := vm.Fetch() instr := vm.Decode(code) vm.Exec(instr) vm.Registers[operand.PC.Addr] += 1 log.Println(vm.Registers[operand.PC.Addr], "|", vm.EndProgramSection) return vm.Registers[operand.PC.Addr] < vm.EndProgramSection } ================================================ FILE: vm/vm_test.go ================================================ package vm import ( "github.com/magiconair/properties/assert" "testing" "tinyscript/gen" "tinyscript/gen/operand" "tinyscript/parser" "tinyscript/translator" ) func TestCalcExpr(t *testing.T) { source := `func main()int{var a = 2 * 3 + 4 return }` taProg := translator.NewTranslator().Translate(parser.Parse(source)) prog := gen.NewOpCodeGen().Gen(taProg) staticTable := prog.GetStaticArea(taProg) opcodes := prog.ToByteCode() vm := NewVM(staticTable, opcodes, prog.Entry) // CALL main vm.runOneStep() vm.runOneStep() vm.runOneStep() t.Log("RA:", vm.Registers[operand.RA.Addr]) assert.Equal(t, vm.GetSpMemory(0), 18) // p0 = 2 * 3 vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, vm.Registers[operand.S0.Addr], 2) assert.Equal(t, vm.Registers[operand.S1.Addr], 3) assert.Equal(t, vm.Registers[operand.L0.Addr], 6) assert.Equal(t, vm.Registers[operand.S2.Addr], 6) assert.Equal(t, vm.GetSpMemory(-2), 6) // p1 = p0 + 4 vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, vm.Registers[operand.S0.Addr], 6) assert.Equal(t, vm.Registers[operand.S1.Addr], 4) assert.Equal(t, vm.Registers[operand.S2.Addr], 10) assert.Equal(t, vm.GetSpMemory(-3), 10) // a = p1 vm.runOneStep() vm.runOneStep() assert.Equal(t, vm.GetSpMemory(-1), 10) assert.Equal(t, vm.Registers[operand.S0.Addr], 10) // RETURN null vm.runOneStep() vm.runOneStep() vm.runOneStep() t.Log("SP:", vm.Registers[operand.SP.Addr]) } func TestRecursiveFunction(t *testing.T) { taProg := translator.NewTranslator().Translate(parser.ParseFromFile("../tests/fact2.ts")) t.Log(taProg) prog := gen.NewOpCodeGen().Gen(taProg) staticTable := prog.GetStaticArea(taProg) opcodes := prog.ToByteCode() t.Log(prog) t.Log(taProg.StaticTable) vm := NewVM(staticTable, opcodes, prog.Entry) // CALL main vm.runOneStep() vm.runOneStep() vm.runOneStep() t.Log("RA:", vm.Registers[operand.RA.Addr]) assert.Equal(t, vm.GetSpMemory(0), 39) // PARAM 10 0 vm.runOneStep() vm.runOneStep() assert.Equal(t, vm.GetSpMemory(-3), 2) // SP -2 vm.runOneStep() vm.runOneStep() t.Log("RA:", vm.Registers[operand.RA.Addr]) // #FUNC_BEGIN vm.runOneStep() assert.Equal(t, vm.GetSpMemory(0), 33) // #p1 = n == 0 assert.Equal(t, vm.GetSpMemory(-1), 2) vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, vm.GetSpMemory(-2) == 0, false) // #IF p1 ELSE L1 vm.runOneStep() vm.runOneStep() // #p3 = n - 1 vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, 1, vm.GetSpMemory(-3)) // #PARAM p3 0 // #SP-5 vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, 1, vm.GetSpMemory(-1)) vm.runOneStep() vm.runOneStep() // #p1 = n == 0 vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, false, vm.GetSpMemory(-2) == 0) // #IF p1 ELSE L1 vm.runOneStep() // #p3 = n - 1 vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() // #PARAM p3 0 vm.runOneStep() vm.runOneStep() vm.runOneStep() // CALL vm.runOneStep() vm.runOneStep() // #p1 = n == 0 vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, true, vm.GetSpMemory(-2) == 0) // #IF p1 ELSE L1 vm.runOneStep() // RETURN 1 vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() // #p4 = p2 * n 计算递归值 vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() // #RETURN p4 vm.runOneStep() vm.runOneStep() //RETURN vm.runOneStep() vm.runOneStep() //#p4 = p2 * n vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() vm.runOneStep() assert.Equal(t, 2, vm.GetSpMemory(-5)) vm.runOneStep() vm.runOneStep() // RETURN MAIN vm.runOneStep() // SP 2 vm.runOneStep() // #RETURN p1 : from main vm.runOneStep() assert.Equal(t, 2, vm.GetSpMemory(-1)) for ; vm.runOneStep(); { } assert.Equal(t, 2, vm.GetSpMemory(0)) } func TestRecursivefunction1(t *testing.T) { taProg := translator.NewTranslator().Translate(parser.ParseFromFile("../tests/fact5.ts")) prog := gen.NewOpCodeGen().Gen(taProg) staticTable := prog.GetStaticArea(taProg) opcodes := prog.ToByteCode() //t.Log(prog) //t.Log(taProg.StaticTable) vm := NewVM(staticTable, opcodes, prog.Entry) vm.run() assert.Equal(t, 120, vm.GetSpMemory(0)) }