1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
#!/usr/bin/env awk -f
# an evaluator of code: scanner, parser and bytecode vm.
BEGIN {
# The prec array contains the precedence level of binary operators.
prec["="] = prec["=="] = prec["!="] = prec["<"] = prec["<="] = prec[">"] = prec[">="] = 1
prec["+="] = prec["-="] = prec["*="] = prec["/="] = prec["%="] = prec["^="] = 1
prec["+"] = prec["-"] = 2
prec["*"] = prec["/"] = prec["%"] = 3
prec["^"] = 4
# The rigth array contains binary operators which are right associative.
right["="] = right["+="] = right["-="] = right["*="] = right["/="] = right["%="] = right["^="] = 1
right["^"] = 1
sp = pc = 0 # sp: stack pointer, pc: program counter.
split("", code)
split("", stack)
split("", sym)
printf "> "
}
{
line = $0
parse()
run()
printf "> "
}
function scan() {
ptok = tok
sub(/^[ \t]+/, "", line)
if (match(line, /^[0-9]([.0-9]*(e[+-]*)*[0-9])*/)) { # Literal number
tok = "d" substr(line, 1, RLENGTH)
} else if (match(line, /^([-=+|&]{2}|![=~]|[<>+-*\/^%]=)/)) { # Operator (2 chars)
tok = substr(line, 1, RLENGTH)
} else if (match(line, /^([][}{)(,+-=?:]|[*\/รท\^])/)) { # Operator (1 char)
tok = substr(line, 1, RLENGTH)
} else if (match(line, /^[A-Za-z_][A-Za-z_0-9]*/)) { # Identifier
tok = "v" substr(line, 1, RLENGTH)
} else if (match(line, /^"(\\.|[^\\"])*"/)) { # Literal string
tok = "s" substr(line, 2, RLENGTH-2)
} else { # Bad token
tok = "b" substr(line, 1, 1)
}
line = substr(line, RLENGTH+1)
return RLENGTH
}
# TODO: unary operators, flow contror
function parse( ops, sl, i, b) {
b = i = length(code)
while (scan() > 0) {
if (tok == "(") {
ops[++sl] = tok
} else if (tok == ")") {
while (sl && ops[sl] != "(") code[i++] = ops[sl--]
sl--
} else if (tok in prec) {
# print "tok:" tok " ptok:" ptok
if (tok ~ /[+-*\/%^]?=/ && ptok ~ /^v/) sub(/^v/, "p", code[i-1]) # mutate var to pointer for assign
# Test precedence against ops, or associativity if same precedence.
while (sl && (prec[tok] < prec[ops[sl]] || prec[tok] == prec[ops[sl]] && !(ops[sl] in right))) {
code[i++] = ops[sl--]
}
ops[++sl] = tok
} else {
code[i++] = tok
}
}
while (sl) code[i++] = ops[sl--]
for (j = b; j < i; j++) printf("%s ", code[j]); print ""
}
# TODO: if, while, function
# Feel like being a stack machine.
function run( c, i, l, t) {
cl = length(code)
while (pc < cl) {
c = code[pc]
if (c == "+") {
stack[--sp] = stack[sp-1] + stack[sp]
} else if (c == "-") {
stack[--sp] = stack[sp-1] - stack[sp]
} else if (c == "*") {
stack[--sp] = stack[sp-1] * stack[sp]
} else if (c == "/") {
stack[--sp] = stack[sp-1] / stack[sp]
} else if (c == "^") {
stack[--sp] = stack[sp-1] ^ stack[sp]
} else if (c == "=") {
stack[--sp] = sym[substr(stack[sp-1], 2)] = stack[sp]
} else if (c == "+=") {
stack[--sp] = sym[substr(stack[sp-1], 2)] += stack[sp]
} else if (c ~ /^p/) {
stack[++sp] = c
} else if (c ~ /^v/) {
stack[++sp] = sym[substr(c, 2)]
} else if (c ~ /^[d]/) {
stack[++sp] = 0 + substr(c, 2)
} else {
print "run: invalid instruction " c
return
}
# printf "c" pc ":%s", c; for (i = 1; i <= sp; i++) printf " " i ":" stack[i]; print ""
# for (i in sym) print "sym[" i "]=" sym[i]
pc++
}
print sym["_"] = stack[sp--]
print "sp: " sp ", len(stack): " length(stack)
# for (i = sp; j in stack; i++) delete stack[i]
}
|