mu/apps/arith.mu

255 lines
5.3 KiB
Forth
Raw Normal View History

2020-06-16 06:53:07 +00:00
# Integer arithmetic using conventional precedence.
#
# Follows part 2 of Jack Crenshaw's "Let's build a compiler!"
# https://compilers.iecc.com/crenshaw
#
# Limitations:
# No division yet.
#
# To build:
# $ ./translate_mu apps/arith.mu
#
# Example session:
# $ ./a.elf
# press ctrl-c or ctrl-d to exit
# > 1
# 1
2020-06-16 06:53:07 +00:00
# > 1+1
# 2
2020-06-16 06:53:07 +00:00
# > 1 + 1
# 2
2020-06-16 06:53:07 +00:00
# > 1+2 +3
# 6
2020-06-16 06:53:07 +00:00
# > 1+2 *3
# 7
2020-06-16 06:53:07 +00:00
# > (1+2) *3
# 9
2020-06-16 06:53:07 +00:00
# > 1 + 3*4
# 13
2020-06-16 06:53:07 +00:00
# > ^D
# $
#
# Error handling is non-existent. This is just a prototype.
2020-06-16 01:05:23 +00:00
fn main -> exit-status/ebx: int {
2020-06-16 02:21:19 +00:00
var look/esi: byte <- copy 0 # lookahead
var n/eax: int <- copy 0 # result of each expression
print-string 0, "press ctrl-c or ctrl-d to exit\n"
2020-06-16 02:21:19 +00:00
# read-eval-print loop
{
2020-06-16 02:21:19 +00:00
# print prompt
print-string 0, "> "
2020-06-16 02:21:19 +00:00
# read and eval
2020-06-16 06:53:07 +00:00
n, look <- simplify # we explicitly thread 'look' everywhere
2020-06-16 02:21:19 +00:00
# if (look == 0) break
compare look, 0
break-if-=
2020-06-16 02:21:19 +00:00
# print
print-int32-decimal 0, n
print-string 0, "\n"
2020-06-16 02:21:19 +00:00
#
loop
}
2020-06-16 01:05:23 +00:00
exit-status <- copy 0
}
2020-06-16 02:21:19 +00:00
fn simplify -> result/eax: int, look/esi: byte {
2020-06-16 06:04:58 +00:00
# prime the pump
2020-06-30 21:54:30 +00:00
look <- get-char
2020-06-16 06:53:07 +00:00
# do it
result, look <- expression look
}
fn expression _look: byte -> result/eax: int, look/esi: byte {
look <- copy _look # should be a no-op
2020-06-16 06:39:05 +00:00
# read arg
2020-06-16 06:06:07 +00:00
result, look <- term look
$expression:loop: {
2020-06-16 06:39:05 +00:00
# while next non-space char in ['+', '-']
2020-06-16 06:04:58 +00:00
look <- skip-spaces look
{
var continue?/eax: boolean <- is-add-or-sub? look
compare continue?, 0 # false
break-if-= $expression:loop
}
2020-06-16 06:39:05 +00:00
# read operator
var op/ecx: byte <- copy 0
2020-06-16 06:04:58 +00:00
op, look <- operator look
2020-06-16 06:39:05 +00:00
# read next arg
2020-06-16 06:04:58 +00:00
var second/edx: int <- copy 0
look <- skip-spaces look
2020-06-16 05:41:38 +00:00
{
2020-06-16 06:04:58 +00:00
var tmp/eax: int <- copy 0
2020-06-16 06:06:07 +00:00
tmp, look <- term look
2020-06-16 06:04:58 +00:00
second <- copy tmp
2020-06-16 05:41:38 +00:00
}
2020-06-16 06:39:05 +00:00
# reduce
$expression:perform-op: {
2020-06-16 06:04:58 +00:00
{
compare op, 0x2b # '+'
break-if-!=
result <- add second
break $expression:perform-op
2020-06-16 06:04:58 +00:00
}
{
compare op, 0x2d # '-'
break-if-!=
result <- subtract second
break $expression:perform-op
2020-06-16 06:04:58 +00:00
}
2020-06-16 05:41:38 +00:00
}
2020-06-16 06:04:58 +00:00
loop
2020-06-16 05:50:54 +00:00
}
2020-06-16 02:21:19 +00:00
look <- skip-spaces look
}
2020-06-16 06:06:07 +00:00
fn term _look: byte -> result/eax: int, look/esi: byte {
look <- copy _look # should be a no-op
2020-06-16 06:39:05 +00:00
# read arg
look <- skip-spaces look
2020-06-16 06:26:13 +00:00
result, look <- factor look
$term:loop: {
2020-06-16 06:39:05 +00:00
# while next non-space char in ['*', '/']
look <- skip-spaces look
{
var continue?/eax: boolean <- is-mul-or-div? look
compare continue?, 0 # false
break-if-= $term:loop
}
2020-06-16 06:39:05 +00:00
# read operator
var op/ecx: byte <- copy 0
op, look <- operator look
2020-06-16 06:39:05 +00:00
# read next arg
var second/edx: int <- copy 0
look <- skip-spaces look
{
var tmp/eax: int <- copy 0
2020-06-16 06:26:13 +00:00
tmp, look <- factor look
second <- copy tmp
}
2020-06-16 06:39:05 +00:00
# reduce
$term:perform-op: {
{
compare op, 0x2a # '*'
break-if-!=
result <- multiply second
break $term:perform-op
}
#? {
#? compare op, 0x2f # '/'
#? break-if-!=
#? result <- divide second # not in Mu yet
#? break $term:perform-op
#? }
}
loop
}
}
2020-06-16 06:26:13 +00:00
fn factor _look: byte -> result/eax: int, look/esi: byte {
$factor:body: {
2020-06-16 06:26:13 +00:00
look <- copy _look # should be a no-op
look <- skip-spaces look
2020-06-16 06:39:05 +00:00
# if next char is not '(', parse a number
compare look, 0x28 # '('
{
break-if-=
result, look <- num look
break $factor:body
}
# otherwise recurse
look <- get-char # '('
result, look <- expression look
look <- skip-spaces look
look <- get-char # ')'
} # $factor:body
2020-06-16 06:26:13 +00:00
}
fn is-mul-or-div? c: byte -> result/eax: boolean {
$is-mul-or-div?:body: {
compare c, 0x2a # '*'
{
break-if-!=
result <- copy 1 # true
break $is-mul-or-div?:body
}
2020-06-16 06:53:07 +00:00
compare c, 0x2f # '/'
{
break-if-!=
result <- copy 1 # true
break $is-mul-or-div?:body
}
result <- copy 0 # false
} # $is-mul-or-div?:body
2020-06-16 06:06:07 +00:00
}
fn is-add-or-sub? c: byte -> result/eax: boolean {
$is-add-or-sub?:body: {
compare c, 0x2b # '+'
{
break-if-!=
result <- copy 1 # true
break $is-add-or-sub?:body
}
compare c, 0x2d # '-'
{
break-if-!=
result <- copy 1 # true
break $is-add-or-sub?:body
}
result <- copy 0 # false
} # $is-add-or-sub?:body
}
2020-06-16 02:37:07 +00:00
fn operator _look: byte -> op/ecx: byte, look/esi: byte {
op <- copy _look
look <- get-char
}
2020-06-16 02:21:19 +00:00
fn num _look: byte -> result/eax: int, look/esi: byte {
2020-06-16 06:54:59 +00:00
look <- copy _look # should be a no-op
var out/edi: int <- copy 0
{
2020-06-16 02:21:19 +00:00
var first-digit/eax: int <- to-decimal-digit look
out <- copy first-digit
}
{
2020-06-16 02:21:19 +00:00
look <- get-char
# done?
var digit?/eax: boolean <- is-decimal-digit? look
2020-06-16 02:21:19 +00:00
compare digit?, 0 # false
2020-06-16 01:05:23 +00:00
break-if-=
# out *= 10
{
var ten/eax: int <- copy 0xa
out <- multiply ten
}
2020-06-16 02:21:19 +00:00
# out += digit(look)
var digit/eax: int <- to-decimal-digit look
out <- add digit
loop
}
result <- copy out
}
2020-06-16 02:21:19 +00:00
fn skip-spaces _look: byte -> look/esi: byte {
look <- copy _look # should be a no-op
{
compare look, 0x20
break-if-!=
look <- get-char
loop
}
}
fn get-char -> look/esi: byte {
2020-09-15 04:42:31 +00:00
var tmp/eax: byte <- read-key-from-real-keyboard
2020-06-16 02:21:19 +00:00
look <- copy tmp
2020-06-16 05:48:07 +00:00
compare look, 0
{
break-if-!=
print-string 0, "^D\n"
2020-06-16 05:48:07 +00:00
syscall_exit
}
2020-06-16 02:21:19 +00:00
}