init
This commit is contained in:
commit
d29226268d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.o
|
47
Makefile
Normal file
47
Makefile
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
.POSIX: # Just kidding, use GNU Make
|
||||||
|
|
||||||
|
CP := cp -f
|
||||||
|
|
||||||
|
CC := cc
|
||||||
|
CFLAGS := -std=c99 -Wall -Wextra -Wpedantic
|
||||||
|
LDFLAGS := -lgmp -lreadline
|
||||||
|
|
||||||
|
OBJ := dc.o
|
||||||
|
|
||||||
|
ifeq ($(DEBUG),1)
|
||||||
|
CFLAGS += -Og -g
|
||||||
|
else
|
||||||
|
CFLAGS += -O3 -funroll-loops -fomit-frame-pointer
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef SAN
|
||||||
|
CFLAGS += -fsanitize=$(SAN)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(FOR_HUMANS),1)
|
||||||
|
CPPFLAGS += -DFOR_HUMANS
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(DC_COMPLY),1)
|
||||||
|
CPPFLAGS += -DDC_COMPLY
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef PROGN
|
||||||
|
CPPFLAGS += -DPROGN=\"$(PROGN)\"
|
||||||
|
else
|
||||||
|
PROGN := dc
|
||||||
|
endif
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(PROGN): $(OBJ)
|
||||||
|
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(LDFLAGS)
|
||||||
|
|
||||||
|
config.h: config.mk.h
|
||||||
|
$(CP) $< $@
|
||||||
|
|
||||||
|
dc.o: config.h ns.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(OBJ) $(PROGN)
|
162
README
Normal file
162
README
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
-- DC --
|
||||||
|
|
||||||
|
desktop calculator implementation
|
||||||
|
|
||||||
|
Copyright 2022, 2023 Emil Williams, see LICENSE
|
||||||
|
|
||||||
|
GMP based DC. Uses readline. No limitations, either.
|
||||||
|
|
||||||
|
-- Notes --
|
||||||
|
|
||||||
|
Note that much of the dc(1) man page is applicable, and is the formal root of
|
||||||
|
this project. It is partly abidded in the featureset, of course this program is
|
||||||
|
not GNU dc or a POSIX implementation* but it closely aligns to the manual page
|
||||||
|
as a primary source of how DC should generally operate.
|
||||||
|
|
||||||
|
Diviations exist, and this should be either treated as a bug, or a feature
|
||||||
|
depending on the case regarding it. If some feature incorrectly matches dc(1)
|
||||||
|
and deliberate incompatibility is not stated then it is a bug. See section
|
||||||
|
Incompats
|
||||||
|
|
||||||
|
Warning : Numbers are purely base 10 and hexadecimal is not implemented in any
|
||||||
|
way but I've Ensured there are no hinderences with A-F with my
|
||||||
|
conflicting character set.
|
||||||
|
|
||||||
|
* There is no POSIX definition of DC, currently. There is a POSIX defition of
|
||||||
|
BC, however. A formal proposal of DC should likely be a reduced subset of
|
||||||
|
GNU/DC, with all normal printing and stack operations, along with registers,
|
||||||
|
strings, and params, with the miscellaneous features and requirements for
|
||||||
|
handling the REPL, files and STDIN all in the same or limited way (such that
|
||||||
|
this program would be complicit) for the CLI. Of course this would come with
|
||||||
|
the specification of multiple precision.
|
||||||
|
|
||||||
|
This section is just a brief airing and goalset of what the codebase can do.
|
||||||
|
|
||||||
|
-- Printing --
|
||||||
|
|
||||||
|
p - print top
|
||||||
|
n - print pop no newline
|
||||||
|
f - print all
|
||||||
|
|
||||||
|
-- Arithmetic --
|
||||||
|
|
||||||
|
n1 is the first popped value, and so on.
|
||||||
|
|
||||||
|
+/-* such that n1 OP n2, pushes 1 result
|
||||||
|
^ n2 to the power of n1, and is not limited
|
||||||
|
v n1's square root
|
||||||
|
|
||||||
|
Note that |, % and ~ have been omitted due to being 'pointless'.
|
||||||
|
They may be added later with mpf_t to mpz_t conversion.
|
||||||
|
|
||||||
|
- Stack --
|
||||||
|
|
||||||
|
c clear
|
||||||
|
d duplicate
|
||||||
|
r reverse n1 and n2
|
||||||
|
z pushes top
|
||||||
|
|
||||||
|
-- Regs --
|
||||||
|
|
||||||
|
any character from \0 to \377 [256] is a valid register and check config for
|
||||||
|
stack max r being any character as stated.
|
||||||
|
|
||||||
|
sr sets a popped value from the primary stack onto r
|
||||||
|
lr gets a value from r and push that onto the stack
|
||||||
|
Sr pushes a popped value unto the registers stack
|
||||||
|
Lr pops a value unto the main stack
|
||||||
|
;r pops two values from the the primary stack, such and pushes n2 at index n1
|
||||||
|
:r pops a value and pushes that index unto the primary stack
|
||||||
|
|
||||||
|
Note that a sr value is not destroyed by Sr. This is technically an
|
||||||
|
incompatibility but I don't see a reason to maintain such abnormality.
|
||||||
|
|
||||||
|
-- Params --
|
||||||
|
|
||||||
|
Push / Pop
|
||||||
|
|
||||||
|
I / i set input radix
|
||||||
|
K / k set the precision (of everything, including registers)
|
||||||
|
|
||||||
|
-- Misc. --
|
||||||
|
|
||||||
|
q quit
|
||||||
|
# a comment.
|
||||||
|
|
||||||
|
-- Incompats --
|
||||||
|
|
||||||
|
- FIXME on i and input radix
|
||||||
|
|
||||||
|
A-F notation will refer to the numbers 10-15,
|
||||||
|
however the Input Radix must be already set higher
|
||||||
|
than these symbols as mpf_set_str does not respect these symbols normally.
|
||||||
|
|
||||||
|
- FIXME on k and precision settings
|
||||||
|
|
||||||
|
the expression: 128k2 3/ ran under this program and GNU DC preduce different outputs,
|
||||||
|
GNU: .66666666666666666666666666666666666666666666666666666666666666666666\
|
||||||
|
666666666666666666666666666666666666666666666666666666666666
|
||||||
|
CACT: 0.6666666666666666666666666666666666666667
|
||||||
|
|
||||||
|
128 refers to apparently a 128 digits of precision, rather than the traditional number of bits.
|
||||||
|
And it rounds up...
|
||||||
|
|
||||||
|
- DISCREPANCY we print a prefixing zero on fractions 1 < & > -1
|
||||||
|
|
||||||
|
- FIXME not very meaningful o
|
||||||
|
|
||||||
|
o has no effect on output.
|
||||||
|
|
||||||
|
- FIXME readline does not take kindly to input
|
||||||
|
|
||||||
|
demo:
|
||||||
|
20S
|
||||||
|
L
|
||||||
|
p
|
||||||
|
|
||||||
|
# loads 20 into register 012's stack
|
||||||
|
# loads that back into the main stack
|
||||||
|
# prints top of stack
|
||||||
|
|
||||||
|
does not have the same effect as when ran as ours, infact, the S call would
|
||||||
|
preduce a clamping error at the CLI REGCHAR level.
|
||||||
|
|
||||||
|
I believe this is an issue with how input is read, thus an issue with how readline filters it's returned string.
|
||||||
|
|
||||||
|
- WARN registers without DC_COMPLY
|
||||||
|
|
||||||
|
Are reduced to a smaller subset and only normal ascii characters are respected (' ' - '~')
|
||||||
|
Only 95 registers out of a naturally presumed 256 registers as stated by DC's man page.
|
||||||
|
|
||||||
|
- INCOMPAT readline respects DEL sometimes
|
||||||
|
|
||||||
|
% echo '^?20' | ./dc
|
||||||
|
% echo '^?20' | dc
|
||||||
|
|
||||||
|
This is the equivalent of UB in your terminal emulator.
|
||||||
|
|
||||||
|
MINOR INCOMPAT FIXME dc prints characters greater than 126 (~) in octal under the format PROGN ": %04o unimplemented\n"
|
||||||
|
|
||||||
|
|
||||||
|
-- TODO --
|
||||||
|
|
||||||
|
Key:
|
||||||
|
- Partial work, nothing tangable
|
||||||
|
~ Partial completion
|
||||||
|
? Experimental stage completion
|
||||||
|
* Completed with confidence
|
||||||
|
No work or consideration has been done
|
||||||
|
X Incompatible completed
|
||||||
|
|
||||||
|
[*] dynamic stack allocation
|
||||||
|
[*] A-F hexadecimal w/ compat
|
||||||
|
[X] Params
|
||||||
|
[*] :;
|
||||||
|
[*] registers
|
||||||
|
[-] digit counts
|
||||||
|
[-] cyclical nrotate R
|
||||||
|
[ ] strings/macros
|
||||||
|
|
||||||
|
-- Findings --
|
||||||
|
|
||||||
|
GNU dc has leaks but it has two orders of magnitude less total space allocated.
|
217
README.2
Normal file
217
README.2
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
-- DC --
|
||||||
|
|
||||||
|
desktop calculator implementation
|
||||||
|
|
||||||
|
Copyright 2022, 2023 Emil Williams, see LICENSE
|
||||||
|
|
||||||
|
GMP based DC. Uses readline. No limitations, either.
|
||||||
|
|
||||||
|
-- Summary --
|
||||||
|
|
||||||
|
DC is a reverse-polish desk calculator which supports unlimited precision arithmetic. Normally DC reads from the
|
||||||
|
standard input; if any command arguments are given to it, they are filenames, and DC reads and executes the contents of
|
||||||
|
the files instead of reading from standard input.
|
||||||
|
|
||||||
|
A reverse-polish calculator stores numbers on a stack. Entering a number pushes it on the stack. Arithmetic operations
|
||||||
|
pop arguments off the stack and push the results.
|
||||||
|
|
||||||
|
To enter a number in DC, type the digits, with an optional decimal point. To enter a negative number, begin the number
|
||||||
|
with `_'. `-' cannot be used for this, as it is a binary operator for subtraction instead. To enter two numbers in
|
||||||
|
succession, separate them with spaces or newlines. These have no meaning as direct commands. Exponential notation is not
|
||||||
|
supported.
|
||||||
|
|
||||||
|
A-F are respected as values under the circumstance that the input radix is higher than their given number.
|
||||||
|
|
||||||
|
-- Invocation --
|
||||||
|
|
||||||
|
DC may be invoked with the following command-line options:
|
||||||
|
|
||||||
|
`-e expr'
|
||||||
|
`-e=expr'
|
||||||
|
`--expression expr'
|
||||||
|
`--expression=expr'
|
||||||
|
Evaluates a DC expression.
|
||||||
|
|
||||||
|
`-f file'
|
||||||
|
`-f=file'
|
||||||
|
`--file file'
|
||||||
|
`--file=file'
|
||||||
|
Reads and evaluates expressions from a file.
|
||||||
|
|
||||||
|
`-h'
|
||||||
|
`--help'
|
||||||
|
Print a usage message summarizing the command-line options, then exits.
|
||||||
|
|
||||||
|
`-V'
|
||||||
|
`--version'
|
||||||
|
Print the current version for DC, then exits.
|
||||||
|
|
||||||
|
The envoriment is reset after every command-line entry.
|
||||||
|
|
||||||
|
-- Printing Commands --
|
||||||
|
|
||||||
|
`p'
|
||||||
|
Prints the value on the top of the stack, without altering the stack. A newline is printed after the value.
|
||||||
|
|
||||||
|
`n' (Supported GNU extension)
|
||||||
|
Prints the value on the top of the stack, popping it off, and does not print a newline after.
|
||||||
|
|
||||||
|
`f'
|
||||||
|
Prints the entire contents of the stack. This is a good command to use if you are lost or want to figure out what the
|
||||||
|
effect of some command has been.
|
||||||
|
|
||||||
|
-- Arithmetic --
|
||||||
|
|
||||||
|
Arithmetic
|
||||||
|
|
||||||
|
`+'
|
||||||
|
Pops two values off the stack, adds them, and pushes the result. The precision of the result is determined only by the
|
||||||
|
values of the arguments, and is enough to be exact.
|
||||||
|
|
||||||
|
`-'
|
||||||
|
Pops two values, subtracts the first one popped from the second one popped, and pushes the result.
|
||||||
|
|
||||||
|
`*'
|
||||||
|
Pops two values, multiplies them, and pushes the result.
|
||||||
|
|
||||||
|
`/'
|
||||||
|
Pops two values, divides the second one popped from the first one popped, and pushes the result.
|
||||||
|
|
||||||
|
`%'
|
||||||
|
Pops two values, computes the remainder of the division that the `/' command would do, and pushes that. The value
|
||||||
|
computed is the same as that computed by the sequence Sd dld/ Ld*- .
|
||||||
|
|
||||||
|
`~' (Supported GNU extension)
|
||||||
|
Pops two values, divides the second one popped from the first one popped. The quotient is pushed first, and the
|
||||||
|
remainder is pushed next.(The sequence SdSn lnld/ LnLd% could also accomplish this function, with slightly different
|
||||||
|
error checking.)
|
||||||
|
|
||||||
|
`^'
|
||||||
|
Pops two values and exponentiates, using the first value popped as the exponent and the second popped as the base. The
|
||||||
|
fraction part of the exponent is ignored.
|
||||||
|
|
||||||
|
FIXME reread and rephrase truness of secondary expression always working or not.
|
||||||
|
`|' (Supported GNU extension)
|
||||||
|
Pops three values and computes a modular exponentiation. The first value popped is used as the reduction modulus; this
|
||||||
|
value must be a non-zero number, and the result may not be accurate if the modulus is not an integer. The second popped
|
||||||
|
is used as the exponent; this value must be a non-negative number, and any fractional part of this exponent will be
|
||||||
|
ignored. The third value popped is the base which gets exponentiated, which should be an integer. For small integers
|
||||||
|
this is like the sequence Sm^Lm%, but, unlike ^, this command will work with arbritrarily large exponents.
|
||||||
|
|
||||||
|
`v'
|
||||||
|
Pops one value, computes its square root, and pushes that.
|
||||||
|
|
||||||
|
|
||||||
|
Most arithmetic operations are affected by the precision value, which you can set with the `k' command. the default
|
||||||
|
precision value is stated within config.h and is usually 128, this refers to bit complexity and not a number of digits.
|
||||||
|
|
||||||
|
-- Additional Mathematical Functions --
|
||||||
|
|
||||||
|
(These are all my extensions.)
|
||||||
|
|
||||||
|
`@'
|
||||||
|
Pops one value, computes its absolute value, and pushes that.
|
||||||
|
|
||||||
|
`\"'
|
||||||
|
Pops one value, ceils the value, and pushes that.
|
||||||
|
|
||||||
|
`''
|
||||||
|
Pops one value, floors the value, and pushes that.
|
||||||
|
|
||||||
|
-- Stack Control --
|
||||||
|
|
||||||
|
`c'
|
||||||
|
Clears the stack, rendering it empty.
|
||||||
|
|
||||||
|
`d'
|
||||||
|
Duplicates the value on the top of the stack, pushing another copy of it. Thus, `4d*p' computes 4 squared and prints
|
||||||
|
it.
|
||||||
|
|
||||||
|
`r' (Supported GNU extension)
|
||||||
|
Reverses the order of (swaps) the top two values on the stack.
|
||||||
|
|
||||||
|
`R' (Supported GNU extension)
|
||||||
|
Rotates the top N items in a cyclical order, negatives do this in reverse.
|
||||||
|
|
||||||
|
-- Registers --
|
||||||
|
|
||||||
|
Under DC_COMPLY, DC provides at least 256 memory registers, each named by a single character. You can store a number in
|
||||||
|
a register and retrieve it later. Without DC_COMPLY there are only 95 registers, being any character between ' ' and '~'
|
||||||
|
(inclusive.)
|
||||||
|
|
||||||
|
`sr'
|
||||||
|
Pop the value off the top of the stack and store it into register r.
|
||||||
|
|
||||||
|
`lr'
|
||||||
|
Copy the value in register r, and push it onto the stack. This does not alter the contents of r. Each register also
|
||||||
|
contains its own stack. The current register value is the top of the register's stack.
|
||||||
|
|
||||||
|
`Sr'
|
||||||
|
Pop the value off the top of the (main) stack and push it onto the stack of register r. The previous value of the
|
||||||
|
register becomes inaccessible.
|
||||||
|
|
||||||
|
`Lr'
|
||||||
|
Pop the value off the top of register r's stack and push it onto the main stack. The previous value in register r's
|
||||||
|
stack, if any, is now accessible via the `lr' command.
|
||||||
|
|
||||||
|
-- Params --
|
||||||
|
|
||||||
|
NOTE THAT INCOMPATIBILITIES EXIST WITHIN SOME OF THESE FEATURES, ESPECIALLY THE OUTPUT RADIX, WHICH DOES NOTHING.
|
||||||
|
|
||||||
|
DC has three parameters that control its operation: the precision, the input radix, and the output radix. The precision
|
||||||
|
specifies the number of fraction digits to keep in the result of most arithmetic operations. The input radix controls the
|
||||||
|
interpretation of numbers typed in; all numbers typed in use this radix. The output radix is used for printing numbers.
|
||||||
|
|
||||||
|
The input and output radices are separate parameters; you can make them unequal, which can be useful or confusing. The
|
||||||
|
input radix must be between 2 and 16 inclusive. The output radix must be at least 2. The precision must be zero or
|
||||||
|
greater. The precision is always measured in decimal digits, regardless of the current input or output radix.
|
||||||
|
|
||||||
|
`i'
|
||||||
|
Pops the value off the top of the stack and uses it to set the input radix.
|
||||||
|
`o'
|
||||||
|
Pops the value off the top of the stack and uses it to set the output radix.
|
||||||
|
`k'
|
||||||
|
Pops the value off the top of the stack and uses it to set the precision.
|
||||||
|
`I'
|
||||||
|
Pushes the current input radix on the stack.
|
||||||
|
`O'
|
||||||
|
Pushes the current output radix on the stack.
|
||||||
|
`K'
|
||||||
|
Pushes the current precision on the stack.
|
||||||
|
|
||||||
|
-- Status Inquiry --
|
||||||
|
|
||||||
|
`Z'
|
||||||
|
Pops a value off the stack, calculates the number of digits it has and pushes that number.
|
||||||
|
`X'
|
||||||
|
Pops a value off the stack, calculates the number of fraction digits it has, and pushes that number.
|
||||||
|
`z'
|
||||||
|
Pushes the current stack depth: the number of objects on the stack before the execution of the `z' command.
|
||||||
|
|
||||||
|
-- Misc --
|
||||||
|
|
||||||
|
`!'
|
||||||
|
Will run the rest of the line as a system command. Note that parsing of the !<, !=, and !> commands take precidence, so
|
||||||
|
if you want to run a command starting with <, =, or > you will need to add a space after the !.
|
||||||
|
|
||||||
|
`#' (Supported GNU extension)
|
||||||
|
Will interpret the rest of the line as a comment.
|
||||||
|
|
||||||
|
`:r'
|
||||||
|
Will pop the top two values off of the stack. The old second-to-top value will be stored in the array r, indexed by the
|
||||||
|
old top-of-stack value.
|
||||||
|
|
||||||
|
`;r'
|
||||||
|
Pops the top-of-stack and uses it as an index into the array r. The selected value is then pushed onto the stack.
|
||||||
|
|
||||||
|
-- COMPILING --
|
||||||
|
|
||||||
|
This project uses GNU Make.
|
||||||
|
|
||||||
|
Building options:
|
||||||
|
|
||||||
|
DEBUG[=1]
|
||||||
|
Enables the debugging detail.
|
||||||
|
|
||||||
|
SAN[=...]
|
||||||
|
Adds a given sanitization.
|
30
config.h
Normal file
30
config.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* config.h - Alter this file as you may need. */
|
||||||
|
|
||||||
|
/* alters features to match closer to GNU dc, incompats exist */
|
||||||
|
/* #define DC_COMPLY */
|
||||||
|
|
||||||
|
/* Reverses the output of f */
|
||||||
|
/* #define FOR_HUMANS */
|
||||||
|
|
||||||
|
/* The default stack size.
|
||||||
|
The suggested value is 4096 (2 11^p). */
|
||||||
|
#define NS_DEFAULT_SIZE 0
|
||||||
|
|
||||||
|
/* The default size of registers, multiply by sizeof mpf_t * NS_REGISTER_MAX.
|
||||||
|
The suggested value is 512 (2 9^p). */
|
||||||
|
#define NS_REG_SIZE 0
|
||||||
|
|
||||||
|
/* The default bit precision of numbers.
|
||||||
|
The high value is 4096 (2 11^p).
|
||||||
|
The lower the cheaper on memory the per-number. */
|
||||||
|
# define NS_DEFAULT_PREC 128
|
||||||
|
|
||||||
|
/* The default formatting for all number outputs. */
|
||||||
|
#define NS_FORMAT_DEC "%.Ff\n"
|
||||||
|
#define NS_FORMAT NS_FORMAT_DEC
|
||||||
|
|
||||||
|
/* Unless PROGN is already defined, define the programs name.
|
||||||
|
Must be known and constant and compile time or it'll seem very inconsistent. */
|
||||||
|
#ifndef PROGN
|
||||||
|
# define PROGN "dc"
|
||||||
|
#endif
|
30
config.mk.h
Normal file
30
config.mk.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* config.h - Alter this file as you may need. */
|
||||||
|
|
||||||
|
/* alters features to match closer to GNU dc, incompats exist */
|
||||||
|
/* #define DC_COMPLY */
|
||||||
|
|
||||||
|
/* Reverses the output of f */
|
||||||
|
/* #define FOR_HUMANS */
|
||||||
|
|
||||||
|
/* The default stack size.
|
||||||
|
The suggested value is 4096 (2 11^p). */
|
||||||
|
#define NS_DEFAULT_SIZE 0
|
||||||
|
|
||||||
|
/* The default size of registers, multiply by sizeof mpf_t * NS_REGISTER_MAX.
|
||||||
|
The suggested value is 512 (2 9^p). */
|
||||||
|
#define NS_REG_SIZE 0
|
||||||
|
|
||||||
|
/* The default bit precision of numbers.
|
||||||
|
The high value is 4096 (2 11^p).
|
||||||
|
The lower the cheaper on memory the per-number. */
|
||||||
|
# define NS_DEFAULT_PREC 128
|
||||||
|
|
||||||
|
/* The default formatting for all number outputs. */
|
||||||
|
#define NS_FORMAT_DEC "%.Ff\n"
|
||||||
|
#define NS_FORMAT NS_FORMAT_DEC
|
||||||
|
|
||||||
|
/* Unless PROGN is already defined, define the programs name.
|
||||||
|
Must be known and constant and compile time or it'll seem very inconsistent. */
|
||||||
|
#ifndef PROGN
|
||||||
|
# define PROGN "dc"
|
||||||
|
#endif
|
348
dc.c
Normal file
348
dc.c
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
/* dc.c - desktop calculator implementation
|
||||||
|
|
||||||
|
Public domain.
|
||||||
|
|
||||||
|
GMP based DC. Uses readline. No limitations, either.
|
||||||
|
|
||||||
|
See README for more details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
|
||||||
|
#include <gmp.h>
|
||||||
|
|
||||||
|
#define DC_EXPORT static
|
||||||
|
#define DC_EXPORT_VAR static
|
||||||
|
|
||||||
|
#ifndef DC_COMPLY
|
||||||
|
# define NS_REG_MAX 95
|
||||||
|
# define NS_REG_OFF 32
|
||||||
|
# define NS_REGCHAR_CLAMP(c) ((c) > NS_REG_MAX)
|
||||||
|
#else
|
||||||
|
# define NS_REG_MAX 256
|
||||||
|
# define NS_REG_OFF 0
|
||||||
|
# define NS_REGCHAR_CLAMP(c) 0
|
||||||
|
#endif /* !DC_COMPLY */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "ns.c"
|
||||||
|
|
||||||
|
#define REGCHAR() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if (!(i + 1 < len)) \
|
||||||
|
{ break; } \
|
||||||
|
c = eval[i + 1]; \
|
||||||
|
++i; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define REGCLAMP() \
|
||||||
|
if (c -= NS_REG_OFF, NS_REGCHAR_CLAMP(c)) \
|
||||||
|
{ \
|
||||||
|
NS_REG_OOB(c, c); \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define AUTHOR "Emil Williams"
|
||||||
|
#define VERSION_STRING "5"
|
||||||
|
#define DC_EXIT (2 << 5)
|
||||||
|
|
||||||
|
/* Command
|
||||||
|
|
||||||
|
Note that I feel this should be ommited, as it doesn't serve
|
||||||
|
any purpose except being able to run commands blindly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
commandn(char * command, size_t len)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
char c;
|
||||||
|
while ((command[i] != '\0' ||
|
||||||
|
command[i] != '\n') &&
|
||||||
|
i < len)
|
||||||
|
{ ++i; }
|
||||||
|
c = command[i+1];
|
||||||
|
command[i+1] = '\0';
|
||||||
|
/* Why we're using system over anything else (direct quote):
|
||||||
|
Will run the rest of the line as a SYSTEM command. */
|
||||||
|
system(command);
|
||||||
|
command[i+1] = c;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
command(char * command)
|
||||||
|
{ return commandn(command, strlen(command)); }
|
||||||
|
|
||||||
|
/* File Helpers */
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
frem(FILE * fp)
|
||||||
|
{
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
return ftell(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT char *
|
||||||
|
slurp(const char * fn, size_t * rlen)
|
||||||
|
{
|
||||||
|
FILE * fp = fopen(fn, "r");
|
||||||
|
if (!fp)
|
||||||
|
{ PERROR_RETURN("fopen", NULL); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t len = frem(fp);
|
||||||
|
char * buf = malloc(len + 1);
|
||||||
|
rewind(fp);
|
||||||
|
if (!buf)
|
||||||
|
{ PERROR_RETURN("malloc", NULL); }
|
||||||
|
if (len != fread(buf, 1, len, fp))
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
{ PERROR_RETURN("fopen", NULL); }
|
||||||
|
}
|
||||||
|
*rlen = len;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DC REPL and CLI->Eval */
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
dcevaln(ns_t * s, char * eval, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int comment = 0;
|
||||||
|
int neg = 0;
|
||||||
|
int lradix = g_iradix;
|
||||||
|
uint8_t c = '\0';
|
||||||
|
for (i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
if (comment)
|
||||||
|
{
|
||||||
|
if (eval[i] == '\n')
|
||||||
|
{ comment = 0; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (eval[i])
|
||||||
|
{
|
||||||
|
case '\t': case '\v': case '\n': case '\r': case ' ':
|
||||||
|
continue;
|
||||||
|
case '_': neg = 1; continue;
|
||||||
|
case '.':
|
||||||
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||||
|
case 'F':
|
||||||
|
/* DC naturally respects numbers like A100 as their hexidecimal value, rather than respecting
|
||||||
|
the base and dropping the number for an invalid numeric symbol. */
|
||||||
|
#ifdef DC_COMPLY
|
||||||
|
lradix = g_iradix;
|
||||||
|
g_iradix = 16;
|
||||||
|
#endif /* DC_COMPLY */
|
||||||
|
/* fallthrough */
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
i += ns_getnum(s, eval+i, len - i, g_iradix);
|
||||||
|
if (neg)
|
||||||
|
{ mpf_neg(NS_PEEK(s), NS_PEEK(s)); }
|
||||||
|
g_iradix = lradix;
|
||||||
|
break;
|
||||||
|
case '#': comment = 1; break;
|
||||||
|
case '*': ns_mul(s); break;
|
||||||
|
case '+': ns_add(s); break;
|
||||||
|
case '-': ns_sub(s); break;
|
||||||
|
case '/': ns_div(s); break;
|
||||||
|
case '^': ns_exp(s); break;
|
||||||
|
/* case '%': ns_mod(s); break; */
|
||||||
|
/* case '~': ns_divrem(s); break; */
|
||||||
|
/* case '|': modexp(s); break; */
|
||||||
|
case 'c': ns_clear(s); break;
|
||||||
|
case 'd': ns_dup(s); break;
|
||||||
|
case 'f': ns_printline_all(s); break;
|
||||||
|
case 'p': ns_printline_peek(s); break;
|
||||||
|
case 'n': ns_print_peek(s); ns_pop(s); break;
|
||||||
|
case 'q': return DC_EXIT;
|
||||||
|
case 'r': ns_reverse(s); break;
|
||||||
|
case 'R': ns_nrotate(s); break;
|
||||||
|
case 'v': ns_sqrt(s); break;
|
||||||
|
case 'z': ns_push_ui(s,s->top); break;
|
||||||
|
case 's': REGCHAR(); REGCLAMP(); ns_reg_set(s, c); break;
|
||||||
|
case 'l': REGCHAR(); REGCLAMP(); ns_reg_get(s, c); break;
|
||||||
|
case 'S': REGCHAR(); REGCLAMP(); ns_reg_push(s, c); break;
|
||||||
|
case 'L': REGCHAR(); REGCLAMP(); ns_reg_pop(s, c); break;
|
||||||
|
case 'k': ns_pop_prec(s); break;
|
||||||
|
case 'K': ns_push_prec(s); break;
|
||||||
|
case 'i': ns_pop_iradix(s); break;
|
||||||
|
case 'I': ns_push_iradix(s); break;
|
||||||
|
case 'o': ns_pop_oradix(s); break;
|
||||||
|
case 'O': ns_push_oradix(s); break;
|
||||||
|
case ':': REGCHAR(); REGCLAMP(); ns_reg_push_index(s, c); break;
|
||||||
|
case ';': REGCHAR(); REGCLAMP(); ns_reg_pop_index(s, c); break;
|
||||||
|
case '!': i += command(eval + i + 1); break;
|
||||||
|
case '?': /* take user input, and execute that
|
||||||
|
as a slave to current level */ break;
|
||||||
|
/* New non-conflicting features not present in DC */
|
||||||
|
case '@': ns_abs(s); break;
|
||||||
|
case '"': ns_ceil(s); break;
|
||||||
|
case '\'': ns_floor(s); break;
|
||||||
|
#ifndef DC_COMPLY
|
||||||
|
/*** CONFLICTION ***/
|
||||||
|
case 'P': (void) ns_pop(s); break;
|
||||||
|
#else
|
||||||
|
case 'P':
|
||||||
|
#endif /* !DC_COMPLY */
|
||||||
|
/* Intended to be implemented */
|
||||||
|
case 'Z': case 'X': case '?': case 'Q':
|
||||||
|
default:
|
||||||
|
if (31 < eval[i] &&
|
||||||
|
eval[i] < 127)
|
||||||
|
{ fprintf(stderr, PROGN ": '%c' (%#o) unimplemented\n", (uint8_t) eval[i], (uint8_t) eval[i]); }
|
||||||
|
else
|
||||||
|
{ fprintf(stderr, PROGN ": (%#o) unimplemented\n", (uint8_t) eval[i]); }
|
||||||
|
}
|
||||||
|
neg = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT inline int
|
||||||
|
dceval(ns_t * s, char * eval)
|
||||||
|
{
|
||||||
|
return dcevaln(s, eval, strlen(eval));
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT inline int
|
||||||
|
dcfile(ns_t * s, char * fn)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
size_t sz;
|
||||||
|
char * buf;
|
||||||
|
buf = slurp(fn, &sz);
|
||||||
|
if (!buf)
|
||||||
|
{ ret = 1; }
|
||||||
|
else
|
||||||
|
{ ret += dcevaln(s, buf, sz); }
|
||||||
|
free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DC CLI and ADDITIONAL INFORMATION */
|
||||||
|
|
||||||
|
#ifndef OTHER_MAIN
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
help(void)
|
||||||
|
{ fprintf(stderr,
|
||||||
|
"Usage: " PROGN " [OPTION] [file ...]\n"
|
||||||
|
"\t-e, --expression[=...] Evaluates an expression\n"
|
||||||
|
"\t-f, --file[=...] Evaluates the contents of file\n"
|
||||||
|
"\t-h, --help Displays this message and exits\n"
|
||||||
|
"\t-V, --version Outputs version information and exits\n"); }
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
version(void)
|
||||||
|
{ fprintf(stderr,
|
||||||
|
PROGN " " VERSION_STRING "\n"
|
||||||
|
"Copyright 2021, 2022, 2023 " AUTHOR "\n\n"
|
||||||
|
|
||||||
|
PROGN " is free software: you can redistribute it and/or modify\n"
|
||||||
|
"it under the terms of the GNU General Public License version 3 as\n"
|
||||||
|
"published by the Free Software Foundation.\n\n"
|
||||||
|
|
||||||
|
"See <https://www.gnu.org/licenses/gpl-3.0.txt>.\n"); }
|
||||||
|
|
||||||
|
#define DC_EQOP(op,arg,off) \
|
||||||
|
if (arg[off] == '=') \
|
||||||
|
{ op(s,arg+off+1); } \
|
||||||
|
else if (arg[off] == '\0' && \
|
||||||
|
argc > 1) \
|
||||||
|
{ \
|
||||||
|
ret = op(s, argv[1]); \
|
||||||
|
++argv; --argc; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ goto help; }
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc,
|
||||||
|
char ** argv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ns_t sreal = (ns_t) {0, -1, NULL};
|
||||||
|
ns_t * s = &sreal;
|
||||||
|
mpf_set_default_prec(g_prec);
|
||||||
|
ns_reg_init();
|
||||||
|
if (!s)
|
||||||
|
{ ret = 1; }
|
||||||
|
else if (argc > 1)
|
||||||
|
{
|
||||||
|
char * arg;
|
||||||
|
while (++argv, --argc)
|
||||||
|
{
|
||||||
|
arg = *argv;
|
||||||
|
if (arg[0] == '-')
|
||||||
|
{
|
||||||
|
if (arg[1] == '-')
|
||||||
|
{
|
||||||
|
if (strcmp(arg+2, "version") == 0)
|
||||||
|
{ goto version; }
|
||||||
|
else if (strcmp(arg+2, "help") == 0)
|
||||||
|
{ goto help; }
|
||||||
|
else if (strncmp(arg+2, "expression", 10) == 0)
|
||||||
|
{ DC_EQOP(dceval,arg,12) }
|
||||||
|
else if (strncmp(arg+2, "file", 4) == 0)
|
||||||
|
{ DC_EQOP(dcfile,arg,6) }
|
||||||
|
}
|
||||||
|
else switch(arg[1])
|
||||||
|
{
|
||||||
|
case 'e': DC_EQOP(dceval,arg,2) break;
|
||||||
|
case 'f': DC_EQOP(dcfile,arg,2) break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, PROGN ": invaild option -- '%c'\n", argv[0][1]);
|
||||||
|
ret = 1;
|
||||||
|
/* fallthrough */
|
||||||
|
help: case 'h': help(); goto stop;
|
||||||
|
version: case 'V': version(); goto stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ dcfile(s, *argv++); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* REPL */
|
||||||
|
{
|
||||||
|
char * input;
|
||||||
|
while (!ret)
|
||||||
|
{
|
||||||
|
input = readline("");
|
||||||
|
if (!input)
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
add_history(input);
|
||||||
|
ret = dceval(s,input);
|
||||||
|
free(input);
|
||||||
|
}
|
||||||
|
clear_history();
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
ns_reg_free();
|
||||||
|
ns_free(s);
|
||||||
|
return ret == DC_EXIT ? 0 : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !OTHER_MAIN */
|
||||||
|
|
||||||
|
#undef REGCHAR
|
||||||
|
#undef REGCLAMP
|
545
ns.c
Normal file
545
ns.c
Normal file
@ -0,0 +1,545 @@
|
|||||||
|
/* ns.c - Number Stack */
|
||||||
|
|
||||||
|
#define NS_PRINTLINE_FORMAT NS_PRINT_FORMAT "\n"
|
||||||
|
|
||||||
|
/* Easy access functions */
|
||||||
|
#define NS_PEEK(s) s->num[s->top]
|
||||||
|
#define NS_PEEKN(s,n) s->num[s->top + n]
|
||||||
|
#define NS_PEEKDOT(s) s.num[s.top]
|
||||||
|
/* Handling of the stack max size and expanding */
|
||||||
|
#define NS_EXPAND(s,n) n >= s->max ? !ns_expand(s,n) : 1
|
||||||
|
#define NS_EXPANDDOT(s,n) n >= s.max ? !ns_expand(&s,n) : 1
|
||||||
|
#define NS_LTMAX(s,n) s->top + n >= s->max ? !ns_expand(s,s->max+n) : 1
|
||||||
|
#define NS_LTMAXDOT(s,n) s.top + n >= s.max ? !ns_expand(&s,s.max+n) : 1
|
||||||
|
/* Debugging and Stack Output */
|
||||||
|
#define NS_DIV0(fail) do { fprintf(stderr, PROGN ": divide by zero\n"); return fail; } while (0)
|
||||||
|
#define NS_REG_UNDERFLOW(fail, c) do { fprintf(stderr, PROGN ": stack register '%c' (%04d) is empty\n", (char) c, c); return fail; } while (0)
|
||||||
|
#ifndef DC_COMPLY
|
||||||
|
#define NS_OVERFLOW(fail) do { fprintf(stderr, PROGN ": overflow\n"); return fail; } while (0)
|
||||||
|
#define NS_UNDERFLOW(fail) do { fprintf(stderr, PROGN ": underflow\n"); return fail; } while (0)
|
||||||
|
#define NS_REG_OOB(fail, c) fprintf(stderr, PROGN ": register '%c' (%04d) is out of bounds\n", (char) c, c)
|
||||||
|
#else
|
||||||
|
#define NS_OVERFLOW(fail) do { fprintf(stderr, PROGN ": at top of stack\n"); return fail; } while (0)
|
||||||
|
#define NS_UNDERFLOW(fail) do { fprintf(stderr, PROGN ": stack empty\n"); return fail; } while (0)
|
||||||
|
#define NS_REG_OOB(fail, c) __builtin_unreachable()
|
||||||
|
#endif /* !DC_COMPLY */
|
||||||
|
#define PERROR_RETURN(str,ret) do { perror(str); return ret; } while (1)
|
||||||
|
#define NS_RETURN(ret) do { perror(PROGN); return ret; } while (1)
|
||||||
|
|
||||||
|
/* #define NS_EXPAND(s,n) \ */
|
||||||
|
/* if (s->top + n >= s->max) \ */
|
||||||
|
/* { ns_expand(s,s->max+n); } */
|
||||||
|
|
||||||
|
#define NS_OP(fn, op) \
|
||||||
|
DC_EXPORT int \
|
||||||
|
fn(ns_t * s) \
|
||||||
|
{ \
|
||||||
|
if (s->top > 0) \
|
||||||
|
{ \
|
||||||
|
--s->top; \
|
||||||
|
op(NS_PEEK(s), \
|
||||||
|
NS_PEEK(s), NS_PEEKN(s,1)); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ NS_UNDERFLOW(1); } \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NS_OP_ONE(fn, op) \
|
||||||
|
DC_EXPORT int \
|
||||||
|
fn(ns_t * s) \
|
||||||
|
{ \
|
||||||
|
if (s->top > -1) \
|
||||||
|
{ \
|
||||||
|
op(NS_PEEK(s), \
|
||||||
|
NS_PEEK(s)); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ NS_UNDERFLOW(1); } \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NS_OP_DIV(fn, op) \
|
||||||
|
DC_EXPORT int fn(ns_t * s) \
|
||||||
|
{ \
|
||||||
|
if (s->top > 0) \
|
||||||
|
{ \
|
||||||
|
if (!(mpf_cmp_ui(NS_PEEK(s), 0) || \
|
||||||
|
mpf_cmp_ui(NS_PEEKN(s,-1), 0))) \
|
||||||
|
{ NS_DIV0(2); } \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
--s->top; \
|
||||||
|
op(NS_PEEK(s), \
|
||||||
|
NS_PEEK(s), NS_PEEKN(s,1)); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ NS_UNDERFLOW(1); } \
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ssize_t max;
|
||||||
|
ssize_t top;
|
||||||
|
mpf_t * num;
|
||||||
|
} ns_t; /* number stack */
|
||||||
|
|
||||||
|
DC_EXPORT_VAR mp_bitcnt_t g_prec = NS_DEFAULT_PREC;
|
||||||
|
DC_EXPORT_VAR int g_iradix = 10;
|
||||||
|
DC_EXPORT_VAR int g_oradix = 10;
|
||||||
|
|
||||||
|
/* Data handling */
|
||||||
|
|
||||||
|
DC_EXPORT void
|
||||||
|
ns_free(ns_t * s)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
/* fprintf(stderr, "%p: Freeing %ld blocks\n", (void *) s, s->max); */
|
||||||
|
for (i = 0; i < s->max; ++i)
|
||||||
|
{
|
||||||
|
mpf_clear(s->num[i]);
|
||||||
|
}
|
||||||
|
free(s->num);
|
||||||
|
/* free(s); */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Growth */
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_expand(ns_t * s, ssize_t newmax)
|
||||||
|
{
|
||||||
|
/* fprintf(stderr, "%p: Expanding to %ld blocks\n", (void *) s, newmax); */
|
||||||
|
s->num = realloc(s->num, newmax * sizeof(mpf_t));
|
||||||
|
if (!s->num)
|
||||||
|
{ NS_RETURN(1); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
for (i = s->max; i < newmax; ++i)
|
||||||
|
{ mpf_init(s->num[i]); }
|
||||||
|
s->max = newmax;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stack */
|
||||||
|
|
||||||
|
#define NS_PUSH(fn, op, type) \
|
||||||
|
DC_EXPORT int \
|
||||||
|
fn(ns_t * s, type val) \
|
||||||
|
{ \
|
||||||
|
if (NS_LTMAX(s,1)) \
|
||||||
|
{ \
|
||||||
|
++s->top; \
|
||||||
|
op(NS_PEEK(s), val); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
NS_OVERFLOW(1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_PUSH(ns_push, mpf_set, mpf_t)
|
||||||
|
NS_PUSH(ns_push_ui, mpf_set_ui, unsigned int)
|
||||||
|
|
||||||
|
DC_EXPORT mpf_t *
|
||||||
|
ns_pop(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > -1)
|
||||||
|
{ return &s->num[s->top--]; }
|
||||||
|
else
|
||||||
|
{ return NULL; }
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT inline int
|
||||||
|
ns_dup(ns_t * s)
|
||||||
|
{
|
||||||
|
if (NS_LTMAX(s,1))
|
||||||
|
{ return ns_push(s, NS_PEEK(s)); }
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT inline void
|
||||||
|
ns_clear(ns_t * s)
|
||||||
|
{ s->top = -1; }
|
||||||
|
|
||||||
|
DC_EXPORT inline int
|
||||||
|
ns_reverse(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > 0)
|
||||||
|
{
|
||||||
|
mpf_swap(NS_PEEK(s), NS_PEEKN(s,-1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nrotate = x - 1
|
||||||
|
top - n swapped with peek -x */
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_nrotate(ns_t * s)
|
||||||
|
{
|
||||||
|
mpf_t * val = ns_pop(s);
|
||||||
|
if (!val)
|
||||||
|
{ NS_UNDERFLOW(1); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
signed long v = mpf_get_si(*val);
|
||||||
|
long i = -v;
|
||||||
|
const long l = -v;
|
||||||
|
while (++i < v)
|
||||||
|
{
|
||||||
|
|
||||||
|
mpf_swap(NS_PEEKN(s,i), NS_PEEKN(s,l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Registers */
|
||||||
|
|
||||||
|
ns_t g_reg[NS_REG_MAX];
|
||||||
|
|
||||||
|
void
|
||||||
|
ns_reg_init(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < NS_REG_MAX; ++i)
|
||||||
|
{
|
||||||
|
g_reg[i].top = -1;
|
||||||
|
g_reg[i].max = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ns_reg_free(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < NS_REG_MAX; ++i)
|
||||||
|
{ ns_free(&g_reg[i]); }
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_reg_set(ns_t * s, int c)
|
||||||
|
{
|
||||||
|
mpf_t * val = ns_pop(s);
|
||||||
|
if (!val)
|
||||||
|
{ NS_REG_UNDERFLOW(1, c); }
|
||||||
|
if (g_reg[c].top > -1)
|
||||||
|
{ mpf_set(NS_PEEKDOT(g_reg[c]), *val); }
|
||||||
|
else
|
||||||
|
{ ns_push(&g_reg[c], *val); }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_reg_get(ns_t * s, int c)
|
||||||
|
{
|
||||||
|
mpf_t * val = ns_pop(&g_reg[c]);
|
||||||
|
if (!val)
|
||||||
|
{ NS_REG_UNDERFLOW(1, c); }
|
||||||
|
if (NS_LTMAX(s,1))
|
||||||
|
{ ns_push(s, *val); }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_reg_push(ns_t * s, int c)
|
||||||
|
{
|
||||||
|
mpf_t * val = ns_pop(s);
|
||||||
|
if (!val)
|
||||||
|
{ NS_REG_UNDERFLOW(1, c); }
|
||||||
|
if (NS_LTMAXDOT(g_reg[c],1))
|
||||||
|
{ ns_push(&g_reg[c], *val); }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_reg_pop(ns_t * s, int c)
|
||||||
|
{
|
||||||
|
mpf_t * val = ns_pop(&g_reg[c]);
|
||||||
|
if (!val)
|
||||||
|
{ NS_REG_UNDERFLOW(1, c); }
|
||||||
|
if (NS_LTMAX(s,1))
|
||||||
|
{ ns_push(s, *val); }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_reg_push_index(ns_t * s, int c)
|
||||||
|
{
|
||||||
|
mpf_t * val, * ip;
|
||||||
|
ip = ns_pop(s);
|
||||||
|
val = ns_pop(s);
|
||||||
|
if (!ip || !val)
|
||||||
|
{ NS_REG_UNDERFLOW(1, c); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ssize_t i = mpf_get_si(*ip);
|
||||||
|
if (NS_EXPANDDOT(g_reg[c],i+1))
|
||||||
|
{
|
||||||
|
mpf_set(g_reg[c].num[i], *val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_reg_pop_index(ns_t * s, int c)
|
||||||
|
{
|
||||||
|
mpf_t * ip = ns_pop(s);
|
||||||
|
if (!ip)
|
||||||
|
{ NS_REG_UNDERFLOW(1, c); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
i = mpf_get_ui(*ip);
|
||||||
|
if (NS_LTMAX(s,1) && NS_EXPANDDOT(g_reg[c],i+1))
|
||||||
|
{
|
||||||
|
ns_push(s, g_reg[c].num[i]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Printing */
|
||||||
|
|
||||||
|
DC_EXPORT void
|
||||||
|
ns_printline_all(ns_t * s)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
#ifdef FOR_HUMANS
|
||||||
|
for (i = 0; i <= s->top; ++i)
|
||||||
|
#else
|
||||||
|
for (i = s->top; i >= 0; --i)
|
||||||
|
#endif /* FOR_HUMANS */
|
||||||
|
{ gmp_printf(NS_FORMAT, s->num[i]); }
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT inline void
|
||||||
|
ns_printline_peek(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > -1)
|
||||||
|
{ gmp_printf(NS_FORMAT, NS_PEEK(s)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT inline int
|
||||||
|
ns_print_peek(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > -1)
|
||||||
|
{
|
||||||
|
gmp_printf(NS_FORMAT, NS_PEEK(s));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef PRINT
|
||||||
|
|
||||||
|
/* Numbers */
|
||||||
|
|
||||||
|
DC_EXPORT inline int
|
||||||
|
ns_isnum(char c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '.':
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||||
|
case 'F':
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT size_t
|
||||||
|
ns_getnum(ns_t * s, char * eval, size_t len, int base)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
char t;
|
||||||
|
while (ns_isnum(eval[i]) && i < len) { ++i; }
|
||||||
|
if (!i)
|
||||||
|
{ return 0; }
|
||||||
|
if (NS_LTMAX(s,1))
|
||||||
|
{
|
||||||
|
++s->top;
|
||||||
|
t = eval[i];
|
||||||
|
eval[i] = '\0';
|
||||||
|
mpf_set_str(NS_PEEK(s), eval, base);
|
||||||
|
eval[i] = t;
|
||||||
|
return i-1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ NS_OVERFLOW(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Digits */
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_digit_last(ns_t * s)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_digit(ns_t * s)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* 0 */
|
||||||
|
|
||||||
|
/* Arithmetic */
|
||||||
|
|
||||||
|
/* handles negative numbers and is multi-precision on both operands unlike mpf_exp_ui */
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_exp(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > 0)
|
||||||
|
{
|
||||||
|
if (!(mpf_cmp_ui(NS_PEEK(s), 0) ||
|
||||||
|
mpf_cmp_ui(s->num[s->top-1], 0)))
|
||||||
|
{ fprintf(stderr, "dc: divide by 0\n"); return 2; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mpf_t i;
|
||||||
|
mpf_t mpf;
|
||||||
|
if (mpf_cmp_ui(NS_PEEK(s), 0) == 0)
|
||||||
|
{ mpf_set_ui(NS_PEEK(s), 1); return 0; }
|
||||||
|
--s->top;
|
||||||
|
mpf_inits(i, mpf, NULL);
|
||||||
|
mpf_set(mpf, NS_PEEK(s));
|
||||||
|
mpf_set(i, s->num[s->top + 1]);
|
||||||
|
mpf_ceil(i, i);
|
||||||
|
mpf_sub_ui(i, i, 1);
|
||||||
|
if (mpf_cmp_ui(i, 0) > 0)
|
||||||
|
{
|
||||||
|
for (; mpf_cmp_ui(i, 0); mpf_sub_ui(i, i, 1))
|
||||||
|
{ mpf_mul(NS_PEEK(s), NS_PEEK(s), mpf); }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (; mpf_cmp_ui(i, 0); mpf_add_ui(i, i, 1))
|
||||||
|
{ mpf_div(NS_PEEK(s), NS_PEEK(s), mpf); }
|
||||||
|
}
|
||||||
|
mpf_clears(i, mpf, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NS_UNDERFLOW(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_OP(ns_add, mpf_add)
|
||||||
|
NS_OP(ns_sub, mpf_sub)
|
||||||
|
NS_OP(ns_mul, mpf_mul)
|
||||||
|
NS_OP_ONE(ns_abs, mpf_abs)
|
||||||
|
NS_OP_ONE(ns_sqrt, mpf_sqrt)
|
||||||
|
NS_OP_ONE(ns_floor, mpf_floor)
|
||||||
|
NS_OP_ONE(ns_ceil, mpf_ceil)
|
||||||
|
NS_OP_DIV(ns_div, mpf_div)
|
||||||
|
|
||||||
|
/* Params */
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_pop_prec(ns_t * s)
|
||||||
|
{
|
||||||
|
ssize_t i, f;
|
||||||
|
mpf_t * mpf = ns_pop(s);
|
||||||
|
if (!mpf)
|
||||||
|
{ NS_UNDERFLOW(1); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t prec = mpf_get_ui(*mpf);
|
||||||
|
/* fprintf(stderr, "new prec: %ld\n", prec); */
|
||||||
|
g_prec = prec;
|
||||||
|
for (i = 0; i < s->max; ++i)
|
||||||
|
{ mpf_set_prec(s->num[i], prec); }
|
||||||
|
for (f = NS_REG_OFF; f < NS_REG_MAX; ++f)
|
||||||
|
{
|
||||||
|
for (i = 0; i < g_reg[f].max; ++i)
|
||||||
|
{ mpf_set_prec(g_reg[f].num[i], prec); }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_push_prec(ns_t * s)
|
||||||
|
{ return ns_push_ui(s, g_prec); }
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_pop_iradix(ns_t * s)
|
||||||
|
{
|
||||||
|
mpf_t * mpf = ns_pop(s);
|
||||||
|
if (!mpf)
|
||||||
|
{ NS_UNDERFLOW(1); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int x = (int) mpf_get_ui(*mpf);
|
||||||
|
if (2 < x && x < INT_MAX - 1)
|
||||||
|
{ fprintf(stderr, PROGN ": input base must be a number between 2 and %d (inclusive)\n", INT_MAX - 1); }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_push_iradix(ns_t * s)
|
||||||
|
{ return ns_push_ui(s, g_iradix); }
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_pop_oradix(ns_t * s)
|
||||||
|
{
|
||||||
|
mpf_t * mpf = ns_pop(s);
|
||||||
|
if (!mpf)
|
||||||
|
{ NS_UNDERFLOW(1); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_oradix = (int) mpf_get_ui(*mpf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_push_oradix(ns_t * s)
|
||||||
|
{ return ns_push_ui(s, g_oradix); }
|
||||||
|
|
||||||
|
/* Test Functions */
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
/* It probably shouldn't return -1 but I'm not worried about this function */
|
||||||
|
DC_EXPORT int
|
||||||
|
ns_ndup(ns_t * s)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
mpf_t dupn;
|
||||||
|
if (!ns_pop(s))
|
||||||
|
{ NS_UNDERFLOW(-1); }
|
||||||
|
for (mpf_set(dupn, NS_PEEKN(s,1));
|
||||||
|
!ret &&
|
||||||
|
mpf_cmp_ui(dupn,1);
|
||||||
|
mpf_sub_ui(dupn,dupn,1))
|
||||||
|
{ ret = ns_dup(s); }
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* 0 */
|
||||||
|
|
||||||
|
/* Definition Cleanup */
|
||||||
|
|
||||||
|
/* #undef NS_OP */
|
||||||
|
/* #undef NS_OP_ONE */
|
||||||
|
/* #undef NS_OP_DIV */
|
||||||
|
/* #undef NS_OVERFLOW */
|
||||||
|
/* #undef NS_UNDERFLOW */
|
||||||
|
/* #undef NS_REG_UNDERFLOW */
|
||||||
|
/* #undef NS_DIV0 */
|
1
old-dc/LICENSE
Normal file
1
old-dc/LICENSE
Normal file
@ -0,0 +1 @@
|
|||||||
|
Public Domain.
|
24
old-dc/Makefile
Normal file
24
old-dc/Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.POSIX: # Just kidding, use GNU Make
|
||||||
|
|
||||||
|
CC := cc
|
||||||
|
CFLAGS := -std=c99 -Wall -Wextra -Wpedantic
|
||||||
|
LDFLAGS := -lm -lgmp -lreadline
|
||||||
|
|
||||||
|
OBJ := dc.o
|
||||||
|
|
||||||
|
ifeq ($(debug),1)
|
||||||
|
CFLAGS += -Og -g
|
||||||
|
else
|
||||||
|
CFLAGS += -O3 -funroll-loops -fomit-frame-pointer
|
||||||
|
endif
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
dc: $(OBJ)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
|
||||||
|
|
||||||
|
dc.o: ns.c arith.c slurp.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(OBJ) dc
|
1
old-dc/add.dc
Normal file
1
old-dc/add.dc
Normal file
@ -0,0 +1 @@
|
|||||||
|
90 120+p
|
104
old-dc/arith.c
Normal file
104
old-dc/arith.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#define ARITH(fn, op) \
|
||||||
|
static inline int \
|
||||||
|
fn(ns_t * s) \
|
||||||
|
{ \
|
||||||
|
if (s->top > 0) \
|
||||||
|
{ \
|
||||||
|
--s->top; \
|
||||||
|
op(NS_PEEK(s), \
|
||||||
|
NS_PEEK(s), s->num[s->top+1]); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
fprintf(stderr, "dc: underflow\n"); \
|
||||||
|
return 1; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARITH_ONE(fn, op) \
|
||||||
|
static inline int \
|
||||||
|
fn(ns_t * s) \
|
||||||
|
{ \
|
||||||
|
if (s->top > -1) \
|
||||||
|
{ \
|
||||||
|
op(NS_PEEK(s), \
|
||||||
|
NS_PEEK(s)); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
fprintf(stderr, "dc: underflow\n"); \
|
||||||
|
return 1; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARITH_DIV(fn, op) \
|
||||||
|
static inline int fn(ns_t * s) \
|
||||||
|
{ \
|
||||||
|
if (s->top > 0) \
|
||||||
|
{ \
|
||||||
|
if (!(mpf_cmp_ui(NS_PEEK(s), 0) || \
|
||||||
|
mpf_cmp_ui(s->num[s->top-1], 0))) \
|
||||||
|
{ fprintf(stderr, "dc: divide by 0\n"); return 2; } \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
--s->top; op(NS_PEEK(s), NS_PEEK(s), s->num[s->top+1]); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ fprintf(stderr, "dc: underflow\n"); return 1; } \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* handles negative numbers and is multi-presision unlike mpf_exp_ui */
|
||||||
|
static inline int ns_exp(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > 0)
|
||||||
|
{
|
||||||
|
if (!(mpf_cmp_ui(NS_PEEK(s), 0) ||
|
||||||
|
mpf_cmp_ui(s->num[s->top-1], 0)))
|
||||||
|
{ fprintf(stderr, "dc: divide by 0\n"); return 2; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mpf_t i;
|
||||||
|
mpf_t mpf;
|
||||||
|
if (mpf_cmp_ui(NS_PEEK(s), 0) == 0)
|
||||||
|
{ mpf_set_ui(NS_PEEK(s), 1); return 0; }
|
||||||
|
--s->top;
|
||||||
|
mpf_inits(i, mpf, NULL);
|
||||||
|
mpf_set(mpf, NS_PEEK(s));
|
||||||
|
mpf_set(i, s->num[s->top + 1]);
|
||||||
|
mpf_ceil(i, i);
|
||||||
|
mpf_sub_ui(i, i, 1);
|
||||||
|
if (mpf_cmp_ui(i, 0) > 0)
|
||||||
|
{
|
||||||
|
for (; mpf_cmp_ui(i, 0); mpf_sub_ui(i, i, 1))
|
||||||
|
{ mpf_mul(NS_PEEK(s), NS_PEEK(s), mpf); }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (; mpf_cmp_ui(i, 0); mpf_add_ui(i, i, 1))
|
||||||
|
{ mpf_div(NS_PEEK(s), NS_PEEK(s), mpf); }
|
||||||
|
}
|
||||||
|
mpf_clears(i, mpf, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ fprintf(stderr, "dc: underflow\n"); return 1; }
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ARITH(ns_add, mpf_add)
|
||||||
|
ARITH(ns_sub, mpf_sub)
|
||||||
|
ARITH(ns_mul, mpf_mul)
|
||||||
|
ARITH_DIV(ns_div, mpf_div)
|
||||||
|
ARITH_DIV(ns_mod, mpf_div)
|
||||||
|
ARITH_ONE(ns_abs, mpf_abs)
|
||||||
|
ARITH_ONE(ns_sqrt, mpf_sqrt)
|
||||||
|
ARITH_ONE(ns_floor, mpf_floor)
|
||||||
|
ARITH_ONE(ns_ceil, mpf_ceil)
|
278
old-dc/dc.c
Normal file
278
old-dc/dc.c
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/* dc.c - desktop calculator implementation
|
||||||
|
|
||||||
|
Public domain.
|
||||||
|
|
||||||
|
-- Printing --
|
||||||
|
|
||||||
|
p - print top
|
||||||
|
n - print pop no newline
|
||||||
|
f - print all
|
||||||
|
|
||||||
|
-- Arithmetic --
|
||||||
|
|
||||||
|
n1 is the first popped value, and so on.
|
||||||
|
|
||||||
|
+/-*% such that n1 OP n2, pushes 1 result
|
||||||
|
^ n2 to the power of n1.
|
||||||
|
v n1's square root
|
||||||
|
|
||||||
|
-- Stack --
|
||||||
|
|
||||||
|
c clear
|
||||||
|
d duplicate
|
||||||
|
r reverse n1 and n2
|
||||||
|
R rotates top n1 items
|
||||||
|
Z pushes n1's digit count
|
||||||
|
X pushes n1's fraction digit count
|
||||||
|
z pushes top
|
||||||
|
|
||||||
|
-- Others --
|
||||||
|
|
||||||
|
b set the operational base, does not alter output
|
||||||
|
q quit
|
||||||
|
# a comment.
|
||||||
|
|
||||||
|
-- currently omitted --
|
||||||
|
|
||||||
|
marcos, strings, and registers.
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
Fix ns_expand
|
||||||
|
Fix CLI to act the same as GNU dc? place original code under DC_COMPLY &
|
||||||
|
implement unified stackframe option
|
||||||
|
Implement registers
|
||||||
|
Implement strings/macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
|
||||||
|
#include <gmp.h>
|
||||||
|
|
||||||
|
#ifndef PROGN
|
||||||
|
# define PROGN "dc"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ns.c"
|
||||||
|
#include "slurp.c"
|
||||||
|
|
||||||
|
#define AUTHOR "Emil Williams"
|
||||||
|
#define VERSION_STRING "3.0"
|
||||||
|
#define DC_EXIT (2 << 5)
|
||||||
|
|
||||||
|
static int
|
||||||
|
isnum(char c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '.':
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
getnum(ns_t * s, char * eval, size_t len, int base)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
char t;
|
||||||
|
while (isnum(eval[i]) && i < len) { ++i; }
|
||||||
|
t = eval[i];
|
||||||
|
eval[i] = '\0';
|
||||||
|
/* fprintf(stderr, "len: %3ld i: %4ld -- %12s --\n", len, i, eval); */
|
||||||
|
NS_EXPAND(s);
|
||||||
|
mpf_set_str(s->num[s->top], eval, base);
|
||||||
|
eval[i] = t;
|
||||||
|
return i-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dcevaln(ns_t * s, char * eval, size_t len)
|
||||||
|
{
|
||||||
|
static int base = 10;
|
||||||
|
size_t i;
|
||||||
|
int comment = 0;
|
||||||
|
int neg = 0;
|
||||||
|
assert(s);
|
||||||
|
for (i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
if (comment)
|
||||||
|
{
|
||||||
|
if (eval[i] == '\n')
|
||||||
|
{ comment = 0; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (eval[i])
|
||||||
|
{
|
||||||
|
case '\t': case '\v': case '\n': case '\r': case ' ':
|
||||||
|
continue;
|
||||||
|
case '_': neg = 1; continue;
|
||||||
|
case '.':
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
i += getnum(s, eval+i, len - i, base);
|
||||||
|
if (neg)
|
||||||
|
{ mpf_neg(NS_PEEK(s), NS_PEEK(s)); }
|
||||||
|
break;
|
||||||
|
case '#': comment = 1; break;
|
||||||
|
case '%': ns_mod(s); break;
|
||||||
|
case '*': ns_mul(s); break;
|
||||||
|
case '+': ns_add(s); break;
|
||||||
|
case '-': ns_sub(s); break;
|
||||||
|
case '/': ns_div(s); break;
|
||||||
|
case '^': ns_exp(s); break;
|
||||||
|
case 'b': base = (int) mpf_get_ui(*ns_pop(s)); break;
|
||||||
|
case 'c': ns_clear(s); break;
|
||||||
|
case 'D': ns_ndup(s); break;
|
||||||
|
case 'd': ns_dup(s); break;
|
||||||
|
case 'f': ns_printline_all(s); break;
|
||||||
|
case 'p': ns_printline_peek(s); break;
|
||||||
|
case 'n': ns_print_peek(s); ns_pop(s); break;
|
||||||
|
case 'q': return DC_EXIT;
|
||||||
|
case 'r': ns_reverse(s); break;
|
||||||
|
case 'v': ns_sqrt(s); break;
|
||||||
|
case 'z': NS_EXPAND(s); mpf_set_ui(s->num[s->top], (unsigned int) s->top); break;
|
||||||
|
#ifndef DC_COMPLY
|
||||||
|
/*** CONFLICTION ***/
|
||||||
|
case 'a': ns_abs(s); break;
|
||||||
|
case 'C': ns_ceil(s); break;
|
||||||
|
case 'F': ns_floor(s); break;
|
||||||
|
case 'P': (void) ns_pop(s); break;
|
||||||
|
#else
|
||||||
|
case 'P':
|
||||||
|
case 'a': case 'C': case 'F':
|
||||||
|
/* Params */
|
||||||
|
case 'i': case 'o': case 'k':
|
||||||
|
case 'I': case 'O': case 'K':
|
||||||
|
#endif /* DC_COMPLY */
|
||||||
|
default:
|
||||||
|
fprintf(stderr, PROGN ": '%c' (%04d) unimplemented\n", eval[i], eval[i]);
|
||||||
|
}
|
||||||
|
neg = 0;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
dceval(ns_t * s, char * eval)
|
||||||
|
{
|
||||||
|
size_t len = strlen(eval);
|
||||||
|
return dcevaln(s, eval, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
help(void)
|
||||||
|
{ fprintf(stderr,
|
||||||
|
"Usage: " PROGN " [OPTION] [file ...]\n"
|
||||||
|
"\t-e expr ..., evaluate expression\n"
|
||||||
|
"\t-f file ..., evaluate contents of file\n"
|
||||||
|
"\t-h, display this help message and exits\n"
|
||||||
|
"\t-V, output version information and exits\n"); }
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
version(void)
|
||||||
|
{ fprintf(stderr,
|
||||||
|
PROGN " " VERSION_STRING "\n"
|
||||||
|
"Copyright 2022, 2023 " AUTHOR "\n\n"
|
||||||
|
|
||||||
|
"This program is free software: you can redistribute it and/or modify\n"
|
||||||
|
"it under the terms of the GNU General Public License version 3 as\n"
|
||||||
|
"published by the Free Software Foundation.\n\n"
|
||||||
|
|
||||||
|
"This program is distributed in the hope that it will be useful,\n"
|
||||||
|
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||||
|
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
||||||
|
"GNU General Public License version 3 for more details.\n\n"
|
||||||
|
|
||||||
|
"You should have received a copy of the GNU General Public License\n"
|
||||||
|
"version 3 along with the source.\n\n"
|
||||||
|
|
||||||
|
"If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.\n"); }
|
||||||
|
|
||||||
|
int
|
||||||
|
dcfile(ns_t * s, int argc, char ** argv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
size_t sz;
|
||||||
|
char * buf;
|
||||||
|
for (i = 0; !ret && i < argc; ++i)
|
||||||
|
{
|
||||||
|
buf = slurp(argv[i], &sz);
|
||||||
|
if (!buf)
|
||||||
|
{ ret = 1; }
|
||||||
|
else
|
||||||
|
{ ret += dcevaln(s, buf, sz); }
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc,
|
||||||
|
char ** argv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ns_t * s;
|
||||||
|
mpf_set_default_prec(2 << MAX_PREC);
|
||||||
|
s = ns_init(NS_DEFAULT);
|
||||||
|
if (!s)
|
||||||
|
{ return 1; }
|
||||||
|
else if (argc > 1)
|
||||||
|
{
|
||||||
|
if (argv[1][0] == '-')
|
||||||
|
{
|
||||||
|
if (argv[1][1] == '-')
|
||||||
|
{
|
||||||
|
if (strcmp(argv[1]+2, "version") == 0)
|
||||||
|
{ goto version; }
|
||||||
|
if (strcmp(argv[1]+2, "help") == 0)
|
||||||
|
{ goto help; }
|
||||||
|
}
|
||||||
|
else switch(argv[1][1])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
case 'e':
|
||||||
|
for (i = 2; !ret && i < argc; ++i)
|
||||||
|
{ ret += dceval(s, argv[i]); }
|
||||||
|
goto stop;
|
||||||
|
case 'f':
|
||||||
|
dcfile(s, argc-=2, argv+=2);
|
||||||
|
goto stop;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, PROGN ": invaild option -- '%c'\n", argv[1][1]);
|
||||||
|
ret = 1;
|
||||||
|
/* fallthrough */
|
||||||
|
help: case 'h': help(); goto stop;
|
||||||
|
version: case 'V': version(); goto stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ dcfile(s, --argc, ++argv); }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char * input;
|
||||||
|
while (!ret)
|
||||||
|
{
|
||||||
|
input = readline("");
|
||||||
|
if (!input || (ret += dceval(s,input)))
|
||||||
|
{ ret = 1; }
|
||||||
|
free(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
ns_free(s);
|
||||||
|
return ret == DC_EXIT ? 0 : ret;
|
||||||
|
}
|
165
old-dc/ns.c
Normal file
165
old-dc/ns.c
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#define NS_DEFAULT (2 << 11) /* 4096 */
|
||||||
|
#define MAX_PREC 4
|
||||||
|
#define PRINT_FORMAT "%.Ff"
|
||||||
|
#define PRINTLINE_FORMAT PRINT_FORMAT "\n"
|
||||||
|
|
||||||
|
#define PERROR_RETURN(str,ret) do { perror(str); return ret; } while (1)
|
||||||
|
|
||||||
|
#define NS_EXPAND(s) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if (s->top + 2 >= (ssize_t) s->max) \
|
||||||
|
{ \
|
||||||
|
if (ns_expand(s, s->max << 2)) \
|
||||||
|
{ return 1; } \
|
||||||
|
if (!s->num) \
|
||||||
|
{ return 1; } \
|
||||||
|
} \
|
||||||
|
++s->top; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define NS_DEINT(s)\
|
||||||
|
|
||||||
|
#define NS_PEEK(s) s->num[s->top]
|
||||||
|
#define NS_PEEKN(s,n) s->num[s->top + n]
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ssize_t max;
|
||||||
|
ssize_t top; /* top < NS_MAX */
|
||||||
|
mpf_t * num;
|
||||||
|
} ns_t; /* number stack */
|
||||||
|
|
||||||
|
static ns_t *
|
||||||
|
ns_init(ssize_t max)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
ns_t * s = (ns_t *) malloc(sizeof(ns_t));
|
||||||
|
if (!s)
|
||||||
|
{ PERROR_RETURN(PROGN, NULL); }
|
||||||
|
s->num = malloc(max * sizeof(mpf_t));
|
||||||
|
if (!s->num)
|
||||||
|
{ PERROR_RETURN(PROGN, NULL); }
|
||||||
|
s->max = max;
|
||||||
|
s->top = -1;
|
||||||
|
for (i = 0; i < s->max; ++i)
|
||||||
|
{ mpf_init(s->num[i]); }
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ns_free(ns_t * s)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
/* fprintf(stderr, "Freeing %ld blocks\n", s->max); */
|
||||||
|
for (i = 0; i < s->max; ++i)
|
||||||
|
{ mpf_clear(s->num[i]); }
|
||||||
|
free(s->num);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ns_expand(ns_t * s, ssize_t newmax)
|
||||||
|
{
|
||||||
|
/* fprintf(stderr, "Expanding to %ld blocks\n", newmax); */
|
||||||
|
assert(newmax > s->max);
|
||||||
|
s->num = realloc(s->num, (size_t) newmax * sizeof(mpf_t));
|
||||||
|
if (!s->num)
|
||||||
|
{ PERROR_RETURN(PROGN, 1); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
for (i = s->max; i < newmax; ++i)
|
||||||
|
{ mpf_init(s->num[i]); }
|
||||||
|
s->max = newmax;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ns_push(ns_t * s, mpf_t val)
|
||||||
|
{
|
||||||
|
NS_EXPAND(s);
|
||||||
|
mpf_set(NS_PEEK(s), val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mpf_t *
|
||||||
|
ns_pop(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > -1)
|
||||||
|
{ return &s->num[s->top--]; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, PROGN ": underflow\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ns_printline_all(ns_t * s)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
#ifdef FOR_HUMANS
|
||||||
|
for (i = 0; i <= s->top; ++i)
|
||||||
|
#else
|
||||||
|
for (i = s->top; i >= 0; --i)
|
||||||
|
#endif /* FOR_HUMANS */
|
||||||
|
{ gmp_printf(PRINTLINE_FORMAT, s->num[i]); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ns_printline_peek(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > -1)
|
||||||
|
{ gmp_printf(PRINTLINE_FORMAT, NS_PEEK(s)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
ns_print_peek(ns_t * s)
|
||||||
|
{
|
||||||
|
if (s->top > -1)
|
||||||
|
{
|
||||||
|
gmp_printf(PRINT_FORMAT, NS_PEEK(s));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ns_clear(ns_t * s)
|
||||||
|
{ s->top = -1; }
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
ns_reverse(ns_t * s)
|
||||||
|
{ if (s->top > 0)
|
||||||
|
{
|
||||||
|
mpf_swap(NS_PEEK(s), s->num[s->top-1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
ns_dup(ns_t * s)
|
||||||
|
{ return ns_push(s, NS_PEEK(s)); }
|
||||||
|
|
||||||
|
/* FIXME REMOVE TEST FUNCTION !!! */
|
||||||
|
static inline int
|
||||||
|
ns_ndup(ns_t * s)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
mpf_t dupn;
|
||||||
|
if (ns_pop(s) == NULL)
|
||||||
|
{ return 1; }
|
||||||
|
for (mpf_set(dupn, NS_PEEKN(s,1));
|
||||||
|
!ret &&
|
||||||
|
mpf_cmp_ui(dupn,1);
|
||||||
|
mpf_sub_ui(dupn,dupn,1))
|
||||||
|
{ ret += ns_dup(s); }
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "arith.c"
|
26
old-dc/slurp.c
Normal file
26
old-dc/slurp.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
static inline size_t
|
||||||
|
frem(FILE * fp)
|
||||||
|
{
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
return ftell(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
slurp(const char * fn, size_t * rlen)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
FILE * fp = fopen(fn, "r");
|
||||||
|
char * buf;
|
||||||
|
if (!fp)
|
||||||
|
{ PERROR_RETURN("fopen", NULL); }
|
||||||
|
if (!(buf = (char *) malloc((len = frem(fp)))))
|
||||||
|
{ PERROR_RETURN("malloc", NULL); }
|
||||||
|
rewind(fp);
|
||||||
|
if (len != fread(buf, 1, len, fp))
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
{ PERROR_RETURN("fopen", NULL); }
|
||||||
|
}
|
||||||
|
*rlen = len;
|
||||||
|
return buf;
|
||||||
|
}
|
17
retrospective
Normal file
17
retrospective
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Project was a more of a exploration of the exact working behind a state
|
||||||
|
machine and a exploration of memory and C, and making a 'real' program,
|
||||||
|
along with a repairing of a former failure and loss of code. This project
|
||||||
|
is not faster, nor comparable to traditional dc.
|
||||||
|
|
||||||
|
I may one day rewrite this with strictly my needs in mind. I have left
|
||||||
|
the code exactly as I had it the last time I've worked on it. I have
|
||||||
|
also left a 'old' copy here, because I didn't have the heart to delete
|
||||||
|
it either. I just decided to upload it, I didn't upload it initial
|
||||||
|
because it did not surpass GNU dc. It's been a while, and I consider
|
||||||
|
it done. Now that it's uploaded I can delete it from my disk.
|
||||||
|
|
||||||
|
Made public domain. Some parts might be buggy. Consider this strictly
|
||||||
|
an archive, no more will be done, this project is deadware and is not
|
||||||
|
to be respected in any capacity, steal and besmirch as you wish.
|
||||||
|
|
||||||
|
Email me if you want to call me dumb: emilwilliams@tuta.io
|
1
test/add.dc
Normal file
1
test/add.dc
Normal file
@ -0,0 +1 @@
|
|||||||
|
3 40 ^p
|
1
test/add.sh
Executable file
1
test/add.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
test `../dc -e '20 120+p'` = '140'
|
1
test/div.sh
Executable file
1
test/div.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
test `../dc -e '10 4/p'` = '2.5'
|
1
test/sub.sh
Executable file
1
test/sub.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
test `../dc -e '20 120-p'` = '-100'
|
Reference in New Issue
Block a user