mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
examples: fix brainvuck.v (handle [ and ] properly); allow both brainvuck.v wasm_codegen/bf_compiler.v to work with .b files too
This commit is contained in:
parent
fae46a8d85
commit
7a49f24eb1
2 changed files with 151 additions and 52 deletions
|
@ -1,66 +1,159 @@
|
||||||
import os
|
import os
|
||||||
|
import term
|
||||||
|
|
||||||
fn main() {
|
// For a more detailed description of the brainfuck language, see:
|
||||||
if os.args.len < 2 {
|
// https://en.wikipedia.org/wiki/Brainfuck
|
||||||
eprintln('you need to supply a brainfuck program as a string argument')
|
// http://brainfuck.org/brainfuck.html ,
|
||||||
exit(1) // exit with non-zero exit code if there is no program to run
|
// http://brainfuck.org/epistle.html ,
|
||||||
}
|
// http://www.hevanet.com/cristofd/brainfuck/ .
|
||||||
program := os.args[1] // our program is fed in as a string
|
|
||||||
|
|
||||||
mut memory := []u8{len: 256} // we have 256 bytes of memory
|
const show_state = os.getenv('VERBOSE') != ''
|
||||||
mut address := u8(0) // as well as an 8-bit address register
|
|
||||||
|
|
||||||
mut stack := []int{} // our stack does not need a maximum length
|
struct BFState {
|
||||||
|
mut:
|
||||||
|
pc u16 // program counter (PC) register
|
||||||
|
address u16 // a 16-bit address register, serving as an index to the memory below
|
||||||
|
program string // the BF program
|
||||||
|
memory []u8 = []u8{len: 65536} // we have 2^16 bytes of memory
|
||||||
|
targets map[int]int // a mapping for the program address of a `[` to its corresponding `]`, and from a `]` to its corresponding opening `[`.
|
||||||
|
}
|
||||||
|
|
||||||
mut program_counter := 0 // program counter
|
fn BFState.new(program string) &BFState {
|
||||||
|
mut state := &BFState{
|
||||||
|
program: program
|
||||||
|
}
|
||||||
|
state.find_matching_pairs()
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
// interpreter starts here
|
// show the current state of an BF interpreter. Useful for debugging.
|
||||||
for program_counter < program.len {
|
fn (state &BFState) show() {
|
||||||
// we look at what the current character our program counter is seeing
|
println('PC: ${state.pc}')
|
||||||
match program[program_counter] {
|
println('Address: ${state.address}')
|
||||||
`>` {
|
mut max_non_zero_address := -1
|
||||||
address++ // increment the address
|
for i := state.memory.len - 1; i >= 0; i-- {
|
||||||
|
if state.memory[i] != 0 {
|
||||||
|
max_non_zero_address = i
|
||||||
|
break
|
||||||
}
|
}
|
||||||
`<` {
|
|
||||||
address-- // decrement the address
|
|
||||||
}
|
|
||||||
`+` {
|
|
||||||
memory[address]++ // increment the value at the address
|
|
||||||
}
|
|
||||||
`-` {
|
|
||||||
memory[address]-- // decrement the value at the address
|
|
||||||
}
|
|
||||||
`.` {
|
|
||||||
print(memory[address].ascii_str()) // print the value at the address
|
|
||||||
}
|
|
||||||
`,` {
|
|
||||||
input := os.input_opt('') or { '' } // read value and account for errors
|
|
||||||
memory[address] = input[0] // this is so we can ignore newlines
|
|
||||||
// because strings are 0-terminated, it also gives us a default value for free!
|
|
||||||
}
|
}
|
||||||
|
println('Memory: ${state.memory#[0..max_non_zero_address + 1]}')
|
||||||
|
println('Memory[Address]: ${state.memory#[state.address..state.address + 1]}')
|
||||||
|
}
|
||||||
|
|
||||||
|
// find_matching_pairs fills in the `targets` mapping for all pairs of `[` and `]`,
|
||||||
|
// so that when interpreting, we would not have to search for them anymore.
|
||||||
|
fn (mut state BFState) find_matching_pairs() {
|
||||||
|
mut stack := []int{}
|
||||||
|
for i in 0 .. state.program.len {
|
||||||
|
pi := state.program[i]
|
||||||
|
match pi {
|
||||||
`[` {
|
`[` {
|
||||||
stack << program_counter // add loop start address to the call stack
|
stack << i
|
||||||
}
|
}
|
||||||
`]` {
|
`]` {
|
||||||
if memory[address] != 0 {
|
if stack.len == 0 {
|
||||||
// set the program counter to the last loop start
|
eprintln('> unmatched `]` found in the program, at position: ${i}')
|
||||||
// so it jumps back and loops again
|
eprintln('program so far:')
|
||||||
program_counter = stack[stack.len - 1]
|
eprintln(state.program#[0..i + 1])
|
||||||
} else {
|
exit(1)
|
||||||
// otherwise remove the address from the stack and continue
|
|
||||||
stack.pop()
|
|
||||||
}
|
}
|
||||||
|
pc := stack.pop()
|
||||||
|
state.targets[pc] = i + 1
|
||||||
|
state.targets[i] = pc + 1
|
||||||
|
// eprintln('>>> found `[` at i $i; pc: $pc')
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stack.len > 0 {
|
||||||
|
eprintln('> found ${stack.len} unmatched `[`:')
|
||||||
|
for i in stack {
|
||||||
|
eprintln(' `[` at position: ${i}, program so far: `${state.program#[0..i + 1]}`')
|
||||||
|
}
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[noreturn]
|
||||||
|
fn (state &BFState) panic_for_bracket(b1 rune, b2 rune) {
|
||||||
|
panic('unbalanced `${b1}` found, its target `${b2}` is not known; address: ${state.address}, pc: ${state.pc}')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut state BFState) run() ? {
|
||||||
|
// the BF interpreter starts here:
|
||||||
|
for state.pc < state.program.len {
|
||||||
|
// get the current program character (corresponding to our program counter), and interpret it according to BF's rules:
|
||||||
|
match state.program[state.pc] {
|
||||||
|
`>` {
|
||||||
|
state.address++ // increment the address
|
||||||
|
}
|
||||||
|
`<` {
|
||||||
|
state.address-- // decrement the address
|
||||||
|
}
|
||||||
|
`+` {
|
||||||
|
state.memory[state.address]++ // increment the value at the address
|
||||||
|
}
|
||||||
|
`-` {
|
||||||
|
state.memory[state.address]-- // decrement the value at the address
|
||||||
|
}
|
||||||
|
`.` {
|
||||||
|
print(rune(state.memory[state.address])) // print the value at the address
|
||||||
|
flush_stdout() // ensure that even single characters are printed immediately, and not buffered
|
||||||
|
}
|
||||||
|
`,` {
|
||||||
|
inp := u8(term.utf8_getchar() or { 0 }) // read a character value from the standard input/terminal
|
||||||
|
state.memory[state.address] = inp
|
||||||
|
}
|
||||||
|
`[` {
|
||||||
|
if state.memory[state.address] == 0 {
|
||||||
|
state.pc = u16(state.targets[state.pc])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`]` {
|
||||||
|
if state.memory[state.address] != 0 {
|
||||||
|
state.pc = u16(state.targets[state.pc])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`#` {
|
||||||
|
state.show()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// the interpreter should ignore characters that are not part of the language
|
// The interpreter should ignore characters that are not part of the language.
|
||||||
|
// I.e. they are treated like programmer comments.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// increment the program counter to go to the next instruction
|
// increment the program counter to go to the next instruction
|
||||||
program_counter++
|
state.pc++
|
||||||
// back to line 20!
|
// go back to the line `for state.pc < state.program.len {`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[noreturn]
|
||||||
|
fn show_usage() {
|
||||||
|
eprintln('you need to supply a brainfuck program/expression as a string argument,')
|
||||||
|
eprintln('or filename.b, if it is located in a file (note the `.b` extension).')
|
||||||
|
exit(1) // exit with non-zero exit code if there is no program to run
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if os.args.len < 2 {
|
||||||
|
show_usage()
|
||||||
|
}
|
||||||
|
mut program := os.args[1] // our program is fed in as a string
|
||||||
|
if program.ends_with('.b') {
|
||||||
|
program = os.read_file(program) or {
|
||||||
|
eprintln('error reading file ${program}: ${err}')
|
||||||
|
show_usage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// print the state of the interpreter at the end
|
mut state := BFState.new(program)
|
||||||
println('Address: ${address}')
|
state.run()
|
||||||
println('Memory: ${memory}')
|
|
||||||
|
if show_state {
|
||||||
|
state.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,13 +104,19 @@ fn generate_code(mut start wasm.Function, bf_expr string) {
|
||||||
|
|
||||||
@[noreturn]
|
@[noreturn]
|
||||||
fn usage() {
|
fn usage() {
|
||||||
eprintln('Usage: bf <expr> <outfile>')
|
eprintln('Usage: bf <expr> <outfile.wasm>')
|
||||||
|
eprintln(' or: bf <path/input.b> <outfile.wasm> (note the `.b` extension)')
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
bf_expr := os.args[1] or { usage() }
|
mut bf_expr := os.args[1] or { usage() }
|
||||||
|
if bf_expr.ends_with('.b') {
|
||||||
|
bf_expr = os.read_file(bf_expr) or {
|
||||||
|
eprintln('file ${bf_expr} could not be read, error: ${err}')
|
||||||
|
usage()
|
||||||
|
}
|
||||||
|
}
|
||||||
outfile := os.args[2] or { usage() }
|
outfile := os.args[2] or { usage() }
|
||||||
|
|
||||||
mut m := wasm.Module{}
|
mut m := wasm.Module{}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue