haiku/build/jam/MathRules
Ingo Weinhold 25b12e83ae Added rules for performing basic integer arithmetics (+, -, *). The rules
operate manually on digit lists, so they are certainly not fast and shouldn't
be used excessively, but at least it's possible to do calculations in Jam now,
should the need arise.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20596 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-04-06 02:37:12 +00:00

606 lines
13 KiB
Plaintext

# Rules providing basic arithmetic operations
HAIKU_ZERO = + 0 ;
HAIKU_ONE = + 1 ;
HAIKU_PAD_9 = 0 1 2 3 4 5 6 7 8 ;
# a > b <==> $(HAIKU_DIGIT_GREATER_$(a)[1$(b)])
HAIKU_DIGIT_GREATER_0 = $(HAIKU_PAD_9) ;
HAIKU_DIGIT_GREATER_1 = $(HAIKU_PAD_9) 1 ;
HAIKU_DIGIT_GREATER_2 = $(HAIKU_PAD_9) 1 1 ;
HAIKU_DIGIT_GREATER_3 = $(HAIKU_PAD_9) 1 1 1 ;
HAIKU_DIGIT_GREATER_4 = $(HAIKU_PAD_9) 1 1 1 1 ;
HAIKU_DIGIT_GREATER_5 = $(HAIKU_PAD_9) 1 1 1 1 1 ;
HAIKU_DIGIT_GREATER_6 = $(HAIKU_PAD_9) 1 1 1 1 1 1 ;
HAIKU_DIGIT_GREATER_7 = $(HAIKU_PAD_9) 1 1 1 1 1 1 1 ;
HAIKU_DIGIT_GREATER_8 = $(HAIKU_PAD_9) 1 1 1 1 1 1 1 1 ;
HAIKU_DIGIT_GREATER_9 = $(HAIKU_PAD_9) 1 1 1 1 1 1 1 1 1 ;
# a + b == $(HAIKU_DIGIT_ADD_$(a)[1$(b)]) (carry ignored)
HAIKU_DIGIT_ADD_0 = $(HAIKU_PAD_9) 0 1 2 3 4 5 6 7 8 9 ;
HAIKU_DIGIT_ADD_1 = $(HAIKU_PAD_9) 1 2 3 4 5 6 7 8 9 0 ;
HAIKU_DIGIT_ADD_2 = $(HAIKU_PAD_9) 2 3 4 5 6 7 8 9 0 1 ;
HAIKU_DIGIT_ADD_3 = $(HAIKU_PAD_9) 3 4 5 6 7 8 9 0 1 2 ;
HAIKU_DIGIT_ADD_4 = $(HAIKU_PAD_9) 4 5 6 7 8 9 0 1 2 3 ;
HAIKU_DIGIT_ADD_5 = $(HAIKU_PAD_9) 5 6 7 8 9 0 1 2 3 4 ;
HAIKU_DIGIT_ADD_6 = $(HAIKU_PAD_9) 6 7 8 9 0 1 2 3 4 5 ;
HAIKU_DIGIT_ADD_7 = $(HAIKU_PAD_9) 7 8 9 0 1 2 3 4 5 6 ;
HAIKU_DIGIT_ADD_8 = $(HAIKU_PAD_9) 8 9 0 1 2 3 4 5 6 7 ;
HAIKU_DIGIT_ADD_9 = $(HAIKU_PAD_9) 9 0 1 2 3 4 5 6 7 8 ;
# a - b == $(HAIKU_DIGIT_SUB_$(a)[1$(b)]) (carry ignored)
HAIKU_DIGIT_SUB_0 = $(HAIKU_PAD_9) 0 9 8 7 6 5 4 3 2 1 ;
HAIKU_DIGIT_SUB_1 = $(HAIKU_PAD_9) 1 0 9 8 7 6 5 4 3 2 ;
HAIKU_DIGIT_SUB_2 = $(HAIKU_PAD_9) 2 1 0 9 8 7 6 5 4 3 ;
HAIKU_DIGIT_SUB_3 = $(HAIKU_PAD_9) 3 2 1 0 9 8 7 6 5 4 ;
HAIKU_DIGIT_SUB_4 = $(HAIKU_PAD_9) 4 3 2 1 0 9 8 7 6 5 ;
HAIKU_DIGIT_SUB_5 = $(HAIKU_PAD_9) 5 4 3 2 1 0 9 8 7 6 ;
HAIKU_DIGIT_SUB_6 = $(HAIKU_PAD_9) 6 5 4 3 2 1 0 9 8 7 ;
HAIKU_DIGIT_SUB_7 = $(HAIKU_PAD_9) 7 6 5 4 3 2 1 0 9 8 ;
HAIKU_DIGIT_SUB_8 = $(HAIKU_PAD_9) 8 7 6 5 4 3 2 1 0 9 ;
HAIKU_DIGIT_SUB_9 = $(HAIKU_PAD_9) 9 8 7 6 5 4 3 2 1 0 ;
# a * b == $(HAIKU_DIGIT_MULT_CARRY_$(a)[1$(b)]) $(HAIKU_DIGIT_MULT_$(a)[1$(b)])
HAIKU_DIGIT_MULT_0 = $(HAIKU_PAD_9) 0 0 0 0 0 0 0 0 0 0 ;
HAIKU_DIGIT_MULT_1 = $(HAIKU_PAD_9) 0 1 2 3 4 5 6 7 8 9 ;
HAIKU_DIGIT_MULT_2 = $(HAIKU_PAD_9) 0 2 4 6 8 0 2 4 6 8 ;
HAIKU_DIGIT_MULT_3 = $(HAIKU_PAD_9) 0 3 6 9 2 5 8 1 4 7 ;
HAIKU_DIGIT_MULT_4 = $(HAIKU_PAD_9) 0 4 8 2 6 0 4 8 2 6 ;
HAIKU_DIGIT_MULT_5 = $(HAIKU_PAD_9) 0 5 0 5 0 5 0 5 0 5 ;
HAIKU_DIGIT_MULT_6 = $(HAIKU_PAD_9) 0 6 2 8 4 0 6 2 8 4 ;
HAIKU_DIGIT_MULT_7 = $(HAIKU_PAD_9) 0 7 4 1 8 5 2 9 6 3 ;
HAIKU_DIGIT_MULT_8 = $(HAIKU_PAD_9) 0 8 6 4 2 0 8 6 4 2 ;
HAIKU_DIGIT_MULT_9 = $(HAIKU_PAD_9) 0 9 8 7 6 5 4 3 2 1 ;
HAIKU_DIGIT_MULT_CARRY_0 = $(HAIKU_PAD_9) 0 0 0 0 0 0 0 0 0 0 ;
HAIKU_DIGIT_MULT_CARRY_1 = $(HAIKU_PAD_9) 0 0 0 0 0 0 0 0 0 0 ;
HAIKU_DIGIT_MULT_CARRY_2 = $(HAIKU_PAD_9) 0 0 0 0 0 1 1 1 1 1 ;
HAIKU_DIGIT_MULT_CARRY_3 = $(HAIKU_PAD_9) 0 0 0 0 1 1 1 2 2 2 ;
HAIKU_DIGIT_MULT_CARRY_4 = $(HAIKU_PAD_9) 0 0 0 1 1 2 2 2 3 3 ;
HAIKU_DIGIT_MULT_CARRY_5 = $(HAIKU_PAD_9) 0 0 1 1 2 2 3 3 4 4 ;
HAIKU_DIGIT_MULT_CARRY_6 = $(HAIKU_PAD_9) 0 0 1 1 2 3 3 4 4 5 ;
HAIKU_DIGIT_MULT_CARRY_7 = $(HAIKU_PAD_9) 0 0 1 2 2 3 4 4 5 6 ;
HAIKU_DIGIT_MULT_CARRY_8 = $(HAIKU_PAD_9) 0 0 1 2 3 4 4 5 6 7 ;
HAIKU_DIGIT_MULT_CARRY_9 = $(HAIKU_PAD_9) 0 0 1 2 3 4 5 6 7 8 ;
# pragma mark - Number Operations
rule NormalizeNum number
{
# NormalizeNum <number> ;
local sign ;
if $(number[1]) = "-" || $(number[1] = "+" {
sign = $(number[1]) ;
number = $(number[2-]) ;
} else {
sign = "+" ;
}
# cut off leading zeros
local number = [ FReverse $(number) ] ;
while $(number[1]) = 0 {
number = $(number[2-]) ;
}
number = [ FReverse $(number) ] ;
# zero is respresented as + 0
if ! $(number) {
number = 0 ;
sign = "+" ;
}
return $(sign) $(number) ;
}
rule Num string : dontExit
{
# Num <number string> [ : <dontExit> ] ;
# split the string into three parts: sign, digits, and remainder
local temp = [ Match ([^0-9]*)([0-9]+)(.*) : $(string) ] ;
# there must be digits, but there must not be a remainder
if ! $(temp[2]) || $(temp[3]) {
if $(dontExit) {
return ;
}
Exit "Not a number" ;
}
# get the sign
local number ;
if ! $(temp[1]) {
number = "+" ;
} else if $(temp[1]) = "+" || $(temp[1]) = "-" {
number = $(temp[1]) ;
} else {
if $(dontExit) {
return ;
}
Exit "Not a number (sign)" ;
}
# split the digits
temp = $(temp[2]) ;
while $(temp[1]) {
# split off the last digit
temp = [ Match (.*)([0-9]) : $(temp[1]) ] ;
number += $(temp[2]) ;
}
# normalize
return [ NormalizeNum $(number) ] ;
}
rule Num2String number
{
# Num2String <number> ;
local sign = $(number[1]) ;
if $(sign) = "+" {
sign = "" ;
}
number = [ FReverse $(number[2-]) ] ;
return $(sign)$(number:J=) ;
}
rule NumGreaterAbs a : b
{
# NumGreaterAbs <a> : <b> ;
# we compare from least to most significant digit
local cmp = 0 ;
while $(a) && $(b) {
# chop off the first digit
local da = $(a[1]:E=0) ;
local db = $(b[1]:E=0) ;
a = $(a[2-]) ;
b = $(b[2-]) ;
if $(da) != $(db) {
if $(HAIKU_DIGIT_GREATER_$(da)[1$(db)]) {
cmp = 1 ;
} else {
cmp = -1 ;
}
}
}
# a is greater, if b is not longer and a is either longer or equally long
# and greater according to the digits comparison
if ! $(b) && ( $(a) || $(cmp) = 1 ) {
return 1 ;
}
return ;
}
rule AddNumAbs a : b
{
# AddNum <a> : <b> ;
local result ;
local carry ;
while $(a) || $(b) || $(carry) {
# chop off the first digit
local da = $(a[1]:E=0) ;
local db = $(b[1]:E=0) ;
a = $(a[2-]) ;
b = $(b[2-]) ;
# add carry to the first digit
if $(carry) {
local daa = $(HAIKU_DIGIT_ADD_1[1$(da)]) ;
carry = $(HAIKU_DIGIT_GREATER_$(da)[1$(daa)]) ;
da = $(daa) ;
}
# add digits
local dr = $(HAIKU_DIGIT_ADD_$(da)[1$(db)]) ;
if $(HAIKU_DIGIT_GREATER_$(da)[1$(dr)]) {
carry = 1 ;
}
result += $(dr) ;
}
return $(result) ;
}
rule SubNumAbs a : b
{
# SubNum <a> : <b> ;
local result ;
local carry ;
while $(a) && ( $(b) || $(carry) ) {
# chop off the first digit
local da = $(a[1]:E=0) ;
local db = $(b[1]:E=0) ;
a = $(a[2-]) ;
b = $(b[2-]) ;
# sub carry from the first digit
if $(carry) {
local daa = $(HAIKU_DIGIT_SUB_$(da)[11]) ;
carry = $(HAIKU_DIGIT_GREATER_$(daa)[1$(da)]) ;
da = $(daa) ;
}
# sub digits
local dr = $(HAIKU_DIGIT_SUB_$(da)[1$(db)]) ;
if $(HAIKU_DIGIT_GREATER_$(dr)[1$(da)]) {
carry = 1 ;
}
result += $(dr) ;
}
if $(b) || $(carry) {
Exit "Error: SubNumAbs: Can't subtract greater from smaller number." ;
}
return $(result) ;
}
rule NegNum a
{
# NegNum <a> ;
if $(a[1]) = "+" {
if $(a) = $(HAIKU_ZERO) {
return $(a) ;
}
return "-" $(a[2-]) ;
} else {
return "+" $(a[2-]) ;
}
}
rule AddNum a : b
{
# AddNum <a> : <b> ;
local signa = $(a[1]) ;
local signb = $(b[1]) ;
a = $(a[2-]) ;
b = $(b[2-]) ;
if $(signa) = $(signb) {
return $(signa) [ AddNumAbs $(a) : $(b) ] ;
} else {
local result ;
if [ NumGreaterAbs $(a) : $(b) ] {
result = $(signa) [ SubNumAbs $(a) : $(b) ] ;
} else {
result = $(signb) [ SubNumAbs $(b) : $(a) ] ;
}
return [ NormalizeNum $(result) ] ;
}
}
rule SubNum a : b
{
# SubNum <a> : <b> ;
return [ AddNum $(a) : [ NegNum $(b) ] ] ;
}
rule MultNumAbsDigit a : digit
{
# MultNumAbsDigit <a> : <digit> ;
local digitMultiples = $(HAIKU_DIGIT_MULT_$(digit)) ;
local digitCarries = $(HAIKU_DIGIT_MULT_CARRY_$(digit)) ;
local result ;
local carry = 0 ;
while $(a) || $(carry) != 0 {
# chop off the first digit
local da = $(a[1]:E=0) ;
a = $(a[2-]) ;
local dr = $(digitMultiples[1$(da)]) ;
# add carry to the resulting digit
if $(carry) {
local dra = $(HAIKU_DIGIT_ADD_$(dr)[1$(carry)]) ;
carry = $(HAIKU_DIGIT_GREATER_$(dr)[1$(dra)]:E=0) ;
dr = $(dra) ;
}
# new carry
carry = $(HAIKU_DIGIT_ADD_$(carry:E=0)[1$(digitCarries[1$(da)])]) ;
result += $(dr) ;
}
return $(result) ;
}
rule MultNum a : b
{
# MultNum <a> : <b> ;
# If one of the factors is 0, we save us computation and normalization.
if $(a) = $(HAIKU_ZERO) || $(b) = $(HAIKU_ZERO) {
return $(HAIKU_ZERO) ;
}
# get the sign for the result
local sign = "+" ;
if $(a[1]) != $(b[1]) {
sign = "-" ;
}
a = $(a[2-]) ;
b = $(b[2-]) ;
# multiply the individual digits of b with a and add up the result
local result = 0 ;
local prefix ;
while $(b) {
local db = $(b[1]) ;
b = $(b[2-]) ;
local adb = $(prefix) [ MultNumAbsDigit $(a) : $(db) ] ;
result = [ AddNumAbs $(result) : $(adb) ] ;
prefix += 0 ;
}
return $(sign) $(result) ;
}
rule NumGreater a : b
{
local signa = $(a[1]) ;
local signb = $(b[1]) ;
a = $(a[2-]) ;
b = $(b[2-]) ;
if $(signa) = $(signb) {
if $(signa) = "+" {
return [ NumGreaterAbs $(a) : $(b) ] ;
} else {
return [ NumGreaterAbs $(b) : $(a) ] ;
}
} else {
if $(signa) = "+" {
return 1 ;
} else {
return ;
}
}
}
# pragma mark - Number String Operations
rule Inc a
{
# Inc <number string a> ;
return [ Num2String [ AddNum [ Num $(a) ] : + 1 ] ] ;
}
rule Dec a
{
# Dec <number string a> ;
return [ Num2String [ AddNum [ Num $(a) ] : - 1 ] ] ;
}
rule Neg a
{
# Neg <number string a> ;
return [ Num2String [ NegNum [ Num $(a) ] ] ] ;
}
rule Add a : b
{
# Add <number string a> : <number string b> ;
return [ Num2String [ AddNum [ Num $(a) ] : [ Num $(b) ] ] ] ;
}
rule Sub a : b
{
# Sub <number string a> : <number string b> ;
return [ Num2String [ SubNum [ Num $(a) ] : [ Num $(b) ] ] ] ;
}
rule Mult a : b
{
# Mult <number string a> : <number string b> ;
return [ Num2String [ MultNum [ Num $(a) ] : [ Num $(b) ] ] ] ;
}
rule Equal a : b
{
# Equal <number string a> : <number string b> ;
if [ Num $(a) ] = [ Num $(b) ] {
return 1 ;
}
return ;
}
rule Less a : b
{
# Less <number string a> : <number string b> ;
return [ NumGreater [ Num $(b) ] : [ Num $(a) ] ] ;
}
rule Greater a : b
{
# Greater <number string a> : <number string b> ;
return [ NumGreater [ Num $(a) ] : [ Num $(b) ] ] ;
}
rule LessOrEqual a : b
{
# LessOrEqual <number string a> : <number string b> ;
if [ NumGreater [ Num $(a) ] : [ Num $(b) ] ] {
return ;
}
return 1 ;
}
rule GreaterOrEqual a : b
{
# GreaterOrEqual <number string a> : <number string b> ;
if [ NumGreater [ Num $(b) ] : [ Num $(a) ] ] {
return ;
}
return 1 ;
}
# pragma mark - Expression Parser
rule ParseAtom expression
{
# ParseAtom <expression> ;
# expression: '(' expression ')' | number
if $(expression[1]) = "(" {
local result = [ ParseExpression $(expression[2-]) ] ;
if $(result[2]) != ")" {
Exit "ParseAtom: Parse error: Expected \")\"." ;
}
return $(result[1]) $(result[3-]) ;
} else {
if ! $(expression) {
Exit "ParseAtom: Parse error: Unexpected end of expression." ;
}
local num = [ Num $(expression[1]) : 1 ] ;
if ! $(num) {
Exit "ParseAtom: Parse error: Expected number instead of:"
$(expression[1]) ;
}
return [ Num2String $(num) ] $(expression[2-]) ;
}
}
rule ParseUnary expression
{
# ParseUnary <expression> ;
# expression: ('+'/'-')* atom
if ! $(expression) {
Exit "ParseUnary: Parse error: Unexpected end of expression." ;
}
# eat all unary "+" and "-" operations
local neg ;
while $(expression[1]) = "+" || $(expression[1]) = "-" {
if $(expression[1]) = "-" {
if $(neg) {
neg = ;
} else {
neg = 1 ;
}
}
expression = $(expression[2-]) ;
}
local result = [ ParseAtom $(expression) ] ;
if $(neg) {
return [ Neg $(result[1]) ] $(result[2-]) ;
}
return $(result) ;
}
rule ParseTerm expression
{
# ParseTerm <expression> ;
# expression: unary ('*' unary)*
local result = [ ParseUnary $(expression) ] ;
local product = $(result[1]) ;
expression = $(result[2-]) ;
while $(expression[1]) = "*" {
# get the operation
local operation ;
operation = Mult ;
# parse the next operand
result = [ ParseUnary $(expression[2-]) ] ;
expression = $(result[2-]) ;
# compute the product
product = [ $(operation) $(product) : $(result[1]) ] ;
}
return $(product) $(expression) ;
}
rule ParseExpression expression
{
# ParseExpression <expression> ;
# expression: term ('+'/'-' term)*
local result = [ ParseTerm $(expression) ] ;
local sum = $(result[1]) ;
expression = $(result[2-]) ;
while $(expression[1]) = "+" || $(expression[1]) = "-" {
# get the operation
local operation ;
if $(expression[1]) = "+" {
operation = Add ;
} else {
operation = Sub ;
}
# parse the next operand
result = [ ParseTerm $(expression[2-]) ] ;
expression = $(result[2-]) ;
# compute the sum
sum = [ $(operation) $(sum) : $(result[1]) ] ;
}
return $(sum) $(expression) ;
}
rule Expr expression
{
# Expr <expression> ;
# tokenize the expression
local tokens ;
for string in $(expression) {
while $(string) {
local split = [ Match "[ \t]*(-|[()+*]|[0-9]*)(.*)" : $(string) ] ;
if ! $(split[1]) {
Exit "Expr: Syntax error: Invalid token: \"$(string)\"." ;
}
tokens += $(split[1]) ;
string = $(split[2]) ;
}
}
local result = [ ParseExpression $(tokens) ] ;
if $(result[2-]) {
Exit "Expr: Garbage at end of expression:" $(result[2-]) ;
}
return $(result[1]) ;
}