Skip to content

Commit 4f68973

Browse files
committed
add byte literal and make a bfvm
1 parent 0775a3d commit 4f68973

File tree

12 files changed

+202
-27
lines changed

12 files changed

+202
-27
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
internal/grammar/.antlr
2-
output
2+
output
3+
.idea

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ test/example:
124124

125125
${CELL} -t riscv tests/examples/multi-files && ckb-debugger --bin multi-files
126126
${CELL} -t riscv tests/examples/import-package && ckb-debugger --bin import-package
127+
128+
${CELL} -t riscv tests/examples/brainfuck-vm.cell && ckb-debugger --bin import-package
129+
${CELL} -t riscv tests/examples/byte.cell && ckb-debugger --bin import-package
130+
127131
test/cross:
128132
@echo " >>> test cross compiling"
129133
@echo cross hi.ll with linking dummy.c

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ brew install openjdk@21
5959
brew install llvm@16
6060
brew tap riscv-software-src/riscv
6161
brew install riscv-tools
62-
export PATH=/opt/homebrew/bin:$PATH
62+
export PATH="/opt/homebrew/bin:$PATH"
63+
export PATH="/opt/homebrew/opt/llvm@16/bin:$PATH"
6364
brew install --cask spike
6465
make build
6566
source install.sh

compiler/compiler/array.go

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package compiler
22

33
import (
4-
"github.com/llir/llvm/ir"
4+
// "github.com/llir/llvm/ir"
55
"github.com/llir/llvm/ir/constant"
6-
"github.com/llir/llvm/ir/enum"
6+
// "github.com/llir/llvm/ir/enum"
77
llvmTypes "github.com/llir/llvm/ir/types"
88
llvmValue "github.com/llir/llvm/ir/value"
99

@@ -143,7 +143,7 @@ func (c *Compiler) compileLoadArrayElement(v *parser.LoadArrayElement) value.Val
143143
panic("unable to LoadArrayElement: could not calculate max length")
144144
}
145145

146-
isCheckedAtCompileTime := false
146+
// isCheckedAtCompileTime := false
147147

148148
if lengthKnownAtCompileTime {
149149
if compileTimeLength < 0 {
@@ -152,7 +152,7 @@ func (c *Compiler) compileLoadArrayElement(v *parser.LoadArrayElement) value.Val
152152

153153
if intType, ok := index.Value.(*constant.Int); ok {
154154
if intType.X.IsInt64() {
155-
isCheckedAtCompileTime = true
155+
// isCheckedAtCompileTime = true
156156

157157
if intType.X.Uint64() > compileTimeLength {
158158
compilePanic("index out of range")
@@ -161,29 +161,30 @@ func (c *Compiler) compileLoadArrayElement(v *parser.LoadArrayElement) value.Val
161161
}
162162
}
163163

164-
if !isCheckedAtCompileTime {
165-
outsideOfLengthBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-array-index-out-of-range")
166-
c.panic(outsideOfLengthBlock, "index out of range")
167-
outsideOfLengthBlock.NewUnreachable()
164+
// if !isCheckedAtCompileTime {
165+
// outsideOfLengthBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-array-index-out-of-range")
166+
// c.panic(outsideOfLengthBlock, "index out of range")
167+
// outsideOfLengthBlock.NewUnreachable()
168168

169-
safeBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-after-array-index-check")
169+
// safeBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-after-array-index-check")
170+
// safeBlock.Term = ir.NewUnreachable()
170171

171-
var runtimeOrCompiletimeCmp *ir.InstICmp
172-
if lengthKnownAtCompileTime {
173-
runtimeOrCompiletimeCmp = c.contextBlock.NewICmp(enum.IPredSGE, indexVal, constant.NewInt(llvmTypes.I32, int64(compileTimeLength)))
174-
} else {
175-
runtimeOrCompiletimeCmp = c.contextBlock.NewICmp(enum.IPredSGE, indexVal, runtimeLength)
176-
}
172+
// var runtimeOrCompiletimeCmp *ir.InstICmp
173+
// if lengthKnownAtCompileTime {
174+
// runtimeOrCompiletimeCmp = c.contextBlock.NewICmp(enum.IPredSGE, indexVal, constant.NewInt(llvmTypes.I32, int64(compileTimeLength)))
175+
// } else {
176+
// runtimeOrCompiletimeCmp = c.contextBlock.NewICmp(enum.IPredSGE, indexVal, runtimeLength)
177+
// }
177178

178-
outOfRangeCmp := c.contextBlock.NewOr(
179-
c.contextBlock.NewICmp(enum.IPredSLT, indexVal, constant.NewInt(llvmTypes.I64, 0)),
180-
runtimeOrCompiletimeCmp,
181-
)
179+
// outOfRangeCmp := c.contextBlock.NewOr(
180+
// c.contextBlock.NewICmp(enum.IPredSLT, indexVal, constant.NewInt(llvmTypes.I64, 0)),
181+
// runtimeOrCompiletimeCmp,
182+
// )
182183

183-
c.contextBlock.NewCondBr(outOfRangeCmp, outsideOfLengthBlock, safeBlock)
184+
// c.contextBlock.NewCondBr(outOfRangeCmp, outsideOfLengthBlock, safeBlock)
184185

185-
c.contextBlock = safeBlock
186-
}
186+
// c.contextBlock = safeBlock
187+
// }
187188

188189
var indicies []llvmValue.Value
189190
if isLlvmArrayBased {

compiler/compiler/constants.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ import (
1414

1515
func (c *Compiler) compileConstantNode(v *parser.ConstantNode) value.Value {
1616
switch v.Type {
17+
case parser.BYTE:
18+
return value.Value{
19+
Value: constant.NewInt(i8.Type, v.Value),
20+
Type: i8,
21+
IsVariable: false,
22+
}
1723
case parser.NUMBER:
1824
var intType *types.Int = i64
1925

compiler/lexer/lexer.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const (
1212
KEYWORD
1313
NUMBER
1414
STRING
15+
BYTE
1516
OPERATOR
1617
EOF
1718
EOL
@@ -34,6 +35,8 @@ func (i Item) String() string {
3435
t = "NUMBER"
3536
case STRING:
3637
t = "STRING"
38+
case BYTE:
39+
t = "BYTE"
3740
case OPERATOR:
3841
t = "OPERATOR"
3942
case EOF:
@@ -99,6 +102,8 @@ var operations = map[string]struct{}{
99102
"..": {}, // is not a real operation. Is there so that ... can be found.
100103
}
101104

105+
var escapedChar = map[byte]byte{'t': '\t', 'n': '\n', 'r': '\r', '\'': '\''}
106+
102107
func Lex(inputFullSource string) []Item {
103108
var res []Item
104109

@@ -174,6 +179,40 @@ func Lex(inputFullSource string) []Item {
174179
continue
175180
}
176181

182+
if input[i] == '\'' {
183+
// String continues until next unescaped '
184+
var str string
185+
186+
i++
187+
188+
for i < len(input) {
189+
if input[i] == '\'' {
190+
break
191+
}
192+
// parse escape char
193+
if input[i] == '\\' {
194+
i++
195+
va, exist := escapedChar[input[i]]
196+
if !exist {
197+
panic("Unsupported escaped character")
198+
}
199+
str = string(va)
200+
i++
201+
continue
202+
}
203+
204+
str = string(input[i])
205+
i++
206+
}
207+
208+
i++
209+
if str == "" {
210+
panic("Ilegal byte")
211+
}
212+
res = append(res, Item{Type: BYTE, Val: string(str), Line: line})
213+
continue
214+
}
215+
177216
// NAME
178217
// Consists of a-z, parse until the last allowed char
179218
if (input[i] >= 'a' && input[i] <= 'z') || (input[i] >= 'A' && input[i] <= 'Z') || input[i] == '_' {

compiler/lexer/lexer_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,30 @@ func TestEscapedString(t *testing.T) {
112112
assert.Equal(t, expected, r)
113113
}
114114

115+
func TestChar(t *testing.T) {
116+
r := Lex(`'a'`)
117+
118+
expected := []Item{
119+
{Type: BYTE, Val: "a", Line: 1},
120+
{Type: EOL},
121+
{Type: EOF},
122+
}
123+
124+
assert.Equal(t, expected, r)
125+
}
126+
127+
func TestEscapedChar(t *testing.T) {
128+
r := Lex(`'\''`)
129+
130+
expected := []Item{
131+
{Type: BYTE, Val: "'", Line: 1},
132+
{Type: EOL},
133+
{Type: EOF},
134+
}
135+
136+
assert.Equal(t, expected, r)
137+
}
138+
115139
func TestLexerSimpleCallWithTwoStrings(t *testing.T) {
116140
r := Lex(`foo("bar", "baz")`)
117141

compiler/parser/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ type DataType uint8
132132

133133
const (
134134
STRING DataType = iota
135+
BYTE
135136
NUMBER
136137
BOOL
137138
)

compiler/parser/parser.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ func (p *parser) parseOneWithOptions(withAheadParse, withArithAhead, withIdentif
8080
}
8181
return
8282

83-
// NUMBER always returns a ConstantNode
84-
// Convert string representation to int64
83+
// NUMBER always returns a ConstantNode
84+
// Convert string representation to int64
8585
case lexer.NUMBER:
8686
val, err := strconv.ParseInt(current.Val, 10, 64)
8787
if err != nil {
@@ -97,7 +97,7 @@ func (p *parser) parseOneWithOptions(withAheadParse, withArithAhead, withIdentif
9797
}
9898
return
9999

100-
// STRING is always a ConstantNode, the value is not modified
100+
// STRING is always a ConstantNode, the value is not modified
101101
case lexer.STRING:
102102
res = &ConstantNode{
103103
Type: STRING,
@@ -107,6 +107,15 @@ func (p *parser) parseOneWithOptions(withAheadParse, withArithAhead, withIdentif
107107
res = p.aheadParse(res)
108108
}
109109
return
110+
case lexer.BYTE:
111+
res = &ConstantNode{
112+
Type: BYTE,
113+
Value: int64(current.Val[0]),
114+
}
115+
if withAheadParse {
116+
res = p.aheadParse(res)
117+
}
118+
return
110119

111120
case lexer.OPERATOR:
112121
if current.Val == "&" {

compiler/parser/var_alloc_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func TestAllocConstGroup(t *testing.T) {
120120
a = 10
121121
b, c = "bbb", "ccc"
122122
d = 20
123+
e, f = '\t', '\''
123124
)`)
124125

125126
expected := &FileNode{
@@ -141,6 +142,11 @@ func TestAllocConstGroup(t *testing.T) {
141142
Val: []Node{&ConstantNode{Type: NUMBER, Value: 20}},
142143
IsConst: true,
143144
},
145+
{
146+
Name: []string{"e", "f"},
147+
Val: []Node{&ConstantNode{Type: BYTE, ValueStr: "\t"}, &ConstantNode{Type: BYTE, ValueStr: "'"}},
148+
IsConst: true,
149+
},
144150
},
145151
},
146152
},

tests/examples/brainfuck-vm.cell

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Test simulating a Turing machine.
2+
package main
3+
4+
import "debug"
5+
6+
// brainfuck
7+
var p, pc int64 = 0, 0 // p for position
8+
var a [30000]uint8
9+
var r string
10+
11+
var prog = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."
12+
13+
function scan(dir int64) {
14+
for nest := dir; (nest * dir) != 0 ; pc += dir {
15+
if prog[pc+dir] == ']' {
16+
nest--
17+
} else if prog[pc+dir] == '[' {
18+
nest++
19+
}
20+
}
21+
}
22+
23+
function main() {
24+
j := 0
25+
for pc = 0; pc < len(prog); pc++ {
26+
if prog[pc] == '>' {
27+
p++
28+
} else if prog[pc] == '<' {
29+
p--
30+
} else if prog[pc] == '+' {
31+
a[p]++
32+
} else if prog[pc] == '-' {
33+
a[p]--
34+
} else if prog[pc] == '.' {
35+
r[j] = a[p]
36+
j++
37+
debug.Printf("%c", a[p])
38+
} else if prog[pc] == '[' {
39+
if a[p] == uint8(0) {
40+
scan(1)
41+
}
42+
} else if prog[pc] == ']' {
43+
if a[p] != uint8(0) {
44+
scan(-1)
45+
}
46+
} else {
47+
debug.Printf("%s", r)
48+
return 0
49+
}
50+
}
51+
debug.Printf("%s", r)
52+
return 0
53+
}

tests/examples/byte.cell

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import "debug"
2+
3+
function main() {
4+
var a = '\''
5+
debug.Printf("%c\n", a)
6+
// var b = a + 3
7+
// debug.Printf("%c\n", b)
8+
b := '*'
9+
b++
10+
b--
11+
if b == '*' {
12+
debug.Printf("b == '*'")
13+
}
14+
var s = "asd"
15+
if s[0] == b {
16+
debug.Printf("s[0] == b")
17+
}
18+
19+
for i := 0; i < len(s); i++ {
20+
debug.Printf("%c", s[i])
21+
}
22+
23+
if s[0] == '+' { // '+'
24+
s[1]++
25+
} else if s[0] == '-' { // '-'
26+
s[1]--
27+
}
28+
29+
return 0
30+
}

0 commit comments

Comments
 (0)