summaryrefslogtreecommitdiff
path: root/ev
blob: 7fcf207b89ba38035600966f618e5ba63bfac7f2 (plain)
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]
}