diff --git a/doc/teapot.lyx b/doc/teapot.lyx index 8c8c726..cec4e05 100644 --- a/doc/teapot.lyx +++ b/doc/teapot.lyx @@ -132,9 +132,9 @@ For ages, spreadsheet programs have been closely associated with financial them, like calculate monitor timings for various resolutions, or produce convincing time statistics which justify the lack of documentation or the need for a budget increase to your employer. - The first part of this user guide explains how the various functions of + The first part of this user guide explains how the various operations of teapot are used, whereas the second part gives an introduction to spreadsheets - and explains the expression evaluator and its functions. + and explains the expression evaluator and its functions and operators. \end_layout \begin_layout Standard @@ -369,7 +369,7 @@ You can think of cells as variables, which value is the value of an associated \end_layout \begin_layout Standard -Spreadsheets offer many editing functions in order to modify, clear, copy +Spreadsheets offer many editing operations in order to modify, clear, copy and move cells or blocks of cells. Besides the usual mathematical functions, there are functions which work on blocks of cells, like calculating the sum of a block or counting all @@ -459,7 +459,7 @@ V Enter \series default key to edit this cell. - A complete list of command mode functions will be given later. + A complete list of command mode operations will be given later. A prompt will appear below the status line: \end_layout @@ -729,8 +729,8 @@ y \shape default is the clocked expression. Don't let this confuse you, as both are entered separately: teapot does - not have an -> operator, but it displays the cell contents this way for - increased overview. + not have an -> operator, but it displays the cell contents this way so + you can easily see both expressions at once. So, give the cell a base expression of \family typewriter 1 @@ -765,7 +765,7 @@ The sheet is currently in reset condition and the result is 1. \begin_layout Standard After this introductory chapter, you should be familiar with the basic concepts in spread sheets. - The next chapters explain all functions available in detail. + The next chapters explain all operations available in detail. You should read them to get an overview of the possibilities offered by teapot. Finally, we will come back to using teapot by showing some common problems @@ -808,7 +808,7 @@ teapot \begin_layout Standard Most notably, a few key bindings don't exist. If something doesn't work as described in here, refer to the pull-down - menus, where all functionality can be found. + menus, where all operations can be found. In addition to the common keys, the GUI variant has extended mouse and keyboard bindings that work similarly to other GUI applications. The table in the following section attempts to summarize all bindings from @@ -821,7 +821,7 @@ Command Mode \begin_layout Standard Right after starting teapot, you are in the command mode. - Many functions from the command mode are also available from menus, but + Many operations from the command mode are also available from menus, but using keys is faster and some things, like moving the cell cursor, are only available through keys. Tables @@ -949,7 +949,7 @@ ASCII Key \begin_inset Text \begin_layout Plain Layout -Function +Operation \end_layout \end_inset @@ -1555,7 +1555,7 @@ ASCII Key \begin_inset Text \begin_layout Plain Layout -Function +Operation \end_layout \end_inset @@ -2007,7 +2007,7 @@ ASCII Key \begin_inset Text \begin_layout Plain Layout -Function +Operation \end_layout \end_inset @@ -2769,8 +2769,8 @@ The Line Editor \end_layout \begin_layout Standard -Many functions in teapot require editing a line of text, e.g. - editing cell contents, typing file names and the line. +Many operations in teapot require editing a line of text, e.g. + editing cell contents, typing file names and the like. Similar to the command mode, all things can be reached by control codes and most by function keys. Table @@ -2820,7 +2820,7 @@ ASCII Key \begin_inset Text \begin_layout Plain Layout -Function +Operation \end_layout \end_inset @@ -3294,7 +3294,7 @@ Key Bindings for the line editor \end_layout \begin_layout Standard -Besides the regular line editor functions, you may use Ctrl-O (Tab in the +Besides the regular line editor operations, you may use Ctrl-O (Tab in the GUI version) to temporarily leave the editor in order to move around in the sheet if you are editing cell contents. Another Ctrl-O (resp. @@ -3312,7 +3312,7 @@ Aborting line editing means that you will return immediately to command \end_layout \begin_layout Section -Interactive Functions +Interactive Operations \end_layout \begin_layout Standard @@ -3337,8 +3337,9 @@ Cells can have several attributes: \begin_layout Itemize A cell label, which is useful because it avoids the need to directly address - cells by their position. - A cell label must be different from function names. + cells by their position, and because it continues to work if the cell is + moved or if parts of the spreadsheet are inserted or deleted. + A cell label must be different from any of the function names. \end_layout @@ -3451,10 +3452,10 @@ Label \end_layout \begin_layout Standard -This function lets you edit the cell label of the current cell. +This operation lets you edit the cell label of the current cell. Further it changes all occurences of it in the cell contents to the new value, unless you erased the cell label. - If a block has been marked by the time you edit the cell label, all occurences + If a block has been marked at the time you edit the cell label, all occurences of the label in contents of cells in that block will be changed. \end_layout @@ -3635,7 +3636,7 @@ Marked blocks of cells can be sorted after one or multiple keys, either dimensional block is sorted row-wise, then horizontal layers will be sorted. The sort key is specified as vector which is orthogonal to the sorted elements, either in ascending or descending order. - The following example illustrates the sort function. + The following example illustrates the sort operation. The upper left part of the screen should look like this: \begin_inset Separator latexpar \end_inset @@ -4025,7 +4026,7 @@ Usually, you want to overwrite the loaded file. Occasionally, you may want to rename a sheet, like before making critical changes or when you load an existing sheet to have a start for making a new one. - The Save As function allows you to save the file under a new name. + The Save As operation allows you to save the file under a new name. \end_layout \begin_layout Subsubsection @@ -4259,7 +4260,7 @@ Alternatively, you can generate a stand-alone document. \end_layout \begin_layout Subsection -Other Functions +Other operations \end_layout \begin_layout Subsubsection @@ -4269,7 +4270,7 @@ Goto Location \begin_layout Standard Sometimes, you directly want to go to a specific position, either to change its contents to see which cell a location expression refers to. - This function lets you enter an expression, which must evaluate to a value + This operation lets you enter an expression, which must evaluate to a value of the type location. If so, the cursor is positioned to that location. For example, you could enter @@ -4289,7 +4290,7 @@ Shell \begin_layout Standard Start a sub shell. Exiting from that sub shell will bring you back into teapot. - This function does not exist in the GUI version. + This operation does not exist in the GUI version. \end_layout \begin_layout Subsubsection @@ -4310,7 +4311,7 @@ If teapot was built with the integrated help viewer, you can access this \end_layout \begin_layout Section -Batch functions +Batch operations \end_layout \begin_layout Standard @@ -4319,7 +4320,7 @@ Besides interactive facilities, teapot has a batch mode. This is handy if you use make(1) to generate a bigger document containing tables, because you don't have to generate a tbl or \SpecialChar LaTeX file each time you - modified a sheet: make will do so. + modify a sheet: make will do so. In batch mode, teapot reads batch commands from standard input. The following commands are available: \end_layout @@ -4724,7 +4725,7 @@ file goto \family default location. - This is the same functionality as the interactive load described in subsection + This has the same functionality as the interactive load described in subsection \emph on 5.12.4 @@ -4850,7 +4851,7 @@ Integer Integer values are exact, their range depends on the C type long \end_layout \begin_layout Description -Location Cell labels and the +Location Cell labels and the result of the \family typewriter &() \family default @@ -4872,8 +4873,8 @@ error() An error always has an assigned error message. Functions and operators, when applied to a value of the type error, evaluate to just that value. - That way, the first error which was found deep inside a complicated expression - will be shown. + That way, the first error which was found, possibly deep inside a complicated + expression, will be shown. \end_layout @@ -4882,13 +4883,13 @@ Operators \end_layout \begin_layout Standard -Unlike other spread sheets, the operators in teapot check the type of the - values they are applied to, which means the try to add a string to a floating +Unlike other spreadsheets, the operators in teapot check the type of the + values they are applied to, which means trying to add a string to a floating point number will result in an type error. Operators on locations in the spreadsheet, which are just triples of integers, generally operate following typical rules for vectors, with particular notes below. - The following operators are available, listed in ascending precendence: + The following operators are available, listed in ascending precedence: \end_layout @@ -4934,7 +4935,7 @@ x \emph default is less than or equal to the corresponding component of \emph on -y + y \emph default and at least one is strictly less. \end_layout @@ -5329,9 +5330,27 @@ argument ) \family default \series default - evaluates to the value of the function applied to the values resulting + evaluates to the value of the function applied to the values resulting from evaluating the argument expressions. + Note that if no arguments are being supplied, the parentheses are optional. + Hence, for example, you can use +\begin_inset Quotes eld +\end_inset + +e +\begin_inset Quotes erd +\end_inset + + by itself, like a reserved constant whose value is the usual mathematical +\begin_inset Quotes eld +\end_inset + +e, +\begin_inset Quotes erd +\end_inset + + the base of natural logarithms. \end_layout \begin_layout Subsection @@ -5358,7 +5377,15 @@ This section documents all available functions in alphabetical order. \begin_layout Description @ \series medium -([int +([location +\emph on + +\begin_inset space ~ +\end_inset + +l +\emph default +,][int \begin_inset space ~ \end_inset @@ -5383,23 +5410,9 @@ y z \emph default ]]]) -\end_layout - -\begin_layout Description -@ -\series medium -(location -\emph on - -\begin_inset space ~ -\end_inset - -l -\emph default -) \series default returns the value of the cell at the specified location. - In the first form, if any of + If any of \emph on x \emph default @@ -5411,8 +5424,8 @@ y \emph on z \emph default - is omitted, the coordinate of the cell is used. - + is specified, that value overrides the corresponding coordinate of the + given location, which defaults to the location of the current cell. \end_layout \begin_layout Description @@ -5426,51 +5439,7 @@ location \series default & \series medium -([int -\emph on - -\begin_inset space ~ -\end_inset - -x -\emph default -][, -\begin_inset space ~ -\end_inset - -[int -\emph on - -\begin_inset space ~ -\end_inset - -y -\emph default -][, -\begin_inset space ~ -\end_inset - -[int -\emph on - -\begin_inset space ~ -\end_inset - -z -\emph default -]]]) -\begin_inset space ~ -\end_inset - -| -\begin_inset space ~ -\end_inset - - -\series default -& -\series medium -(location +([location \emph on \begin_inset space ~ @@ -5478,72 +5447,120 @@ z l \emph default -) +,][int +\emph on + +\begin_inset space ~ +\end_inset + +x +\emph default +][, +\begin_inset space ~ +\end_inset + +[int +\emph on + +\begin_inset space ~ +\end_inset + +y +\emph default +][, +\begin_inset space ~ +\end_inset + +[int +\emph on + +\begin_inset space ~ +\end_inset + +z +\emph default +]]]) \series default -returns the specified location. - In the first form, if any of +the arguments are interpreted in exactly the same way as for +\series bold +@ +\series default +(), but this fuction returns the location, not the value of the cell at + that location. + So note for example that a bare +\series bold +& +\series default + with no arguments or even parentheses evaluates to the location of the + current cell. +\end_layout + +\begin_layout Description +D( +\series medium +[location +\emph on + +\begin_inset space ~ +\end_inset + +l +\emph default +,][int +\begin_inset space ~ +\end_inset + + \emph on x \emph default -, +][,[int \emph on + +\begin_inset space ~ +\end_inset + y +\emph default +][,[int +\emph on + +\begin_inset space ~ +\end_inset + +z +\emph default +]]] +\series default +) Like +\series bold +& +\series default +(), except that any of +\emph on +x, y, \emph default or \emph on z \emph default - is omitted, the coordinate of the cell is used. - The second form is in fact a no-op, but it is allowed for convenience and - consistency of expressions. -\end_layout - -\begin_layout Description -R() Exactly the same as -\series bold -@ -\series default -, except the coordinates of the argument are interpreted as an offset to - the current location (rather than as absolute coordinates in the sheet); - think -\begin_inset Quotes eld -\end_inset - -R -\begin_inset Quotes erd -\end_inset - - for -\begin_inset Quotes eld -\end_inset - -relative. -\begin_inset Quotes erd -\end_inset - - Thus + specified are added to the given location, which defaults to the current + location; and as a special case, if only the location is given, i.e. + \family sans \series bold -R -\series default -(-1) +D \family default - returns the value of the cell immediately to the left of this one, and -\family sans -\series bold - R \series default -(,,1) -\family default - returns the same cell as this one but on the following layer. -\end_layout - -\begin_layout Description -D() Exactly the same as -\series bold -& -\series default -, except the coordinates of the argument are added to the current location. +( +\emph on +l +\emph default +), then the location +\emph on +l +\emph default + is added to the current location. Think \begin_inset Quotes eld \end_inset @@ -5574,7 +5591,101 @@ D \series default (,2,1) \family default - returns the location of the cell two below this one on the following layer. + returns the location of the cell two below this one on the following layer, + as does +\family sans +\series bold +D +\family default +\series default +(&(0,2,1)), but +\family sans +\series bold +D +\family default +\series default +(TABLE,1) returns the location of the cell just to the right of the one + labeled +\begin_inset Quotes eld +\end_inset + +TABLE +\begin_inset Quotes erd +\end_inset + +. +\end_layout + +\begin_layout Description +R( +\series medium +\emph on +args +\series default +\emph default +) Shorthand for +\series bold +@ +\series default +( +\family sans +\series bold +D +\family default +\series default +(args)). + Think +\begin_inset Quotes eld +\end_inset + +R +\begin_inset Quotes erd +\end_inset + + for +\begin_inset Quotes eld +\end_inset + +relative. +\begin_inset Quotes erd +\end_inset + + Thus +\family sans +\series bold +R +\series default +(-1) +\family default + returns the value of the cell immediately to the left of this one, and +\family sans +\series bold + R +\series default +(,,1) +\family default + returns the same cell as this one but on the following layer, as does +\family sans +\series bold +R +\series default +(&(0,0,1) +\family default +), but +\family sans +\series bold +R +\family default +\series default +(TABLE,,1) returns the value of the cell just below the one labeled +\begin_inset Quotes eld +\end_inset + +TABLE +\begin_inset Quotes erd +\end_inset + +. \end_layout \begin_layout Description @@ -5726,7 +5837,10 @@ X There is a corresponding location function \family sans -X&() +\series bold +X& +\series default +() \family default as well, which takes exactly the same arguments with the same meanings, but it is rarely needed. @@ -6085,6 +6199,35 @@ or of all the supplied values. \end_layout +\begin_layout Description + +\series medium +float +\begin_inset space ~ +\end_inset + + +\series default +ceil +\series medium +(float +\emph on + +\begin_inset space ~ +\end_inset + +x +\emph default +) evaluates to the smallest integral floating-point value greater than or + equal to +\family sans +\series default +\emph on +x +\emph default +. +\end_layout + \begin_layout Description clock \series medium @@ -6101,7 +6244,14 @@ condition conditionally clocks the specified cell if the condition is not 0. If two locations are given, all cells in that range will be clocked. The return value of clock is empty. - + Note that the clocked expression of a cell can clock itself; indeed, that's + the only way that a cell can be clocked more than once in a single recalculatio +n. + Take care with the +\emph on +condition +\emph default + expression to avoid recalculation becoming stuck in an infinite loop, however. \end_layout \begin_layout Description @@ -6170,6 +6320,11 @@ x \end_layout +\begin_layout Description +decimal used as a keyword to the string() function; listed here to record + that this identifier may not be used as a cell label. +\end_layout + \begin_layout Description \series medium @@ -6210,14 +6365,14 @@ float \series default e \series medium -() +[()] \series default evaluates to the Euler constant \emph on e \emph default . - + Note the parentheses are optional. \end_layout \begin_layout Description @@ -6248,7 +6403,7 @@ message \begin_layout Description eval \series medium -(location) +(location) \series default evaluates to the value of the expression in the cell at the given \emph on @@ -6270,7 +6425,7 @@ float \series default float \series medium -(string +(string|int \emph on \begin_inset space ~ @@ -6278,10 +6433,37 @@ float s \emph default -) +) \series default - converts the given string into a floating point number. - + converts the given string or int into a floating point number. +\end_layout + +\begin_layout Description + +\series medium +float +\begin_inset space ~ +\end_inset + + +\series default +floor +\series medium +(float +\emph on + +\begin_inset space ~ +\end_inset + +x +\emph default +) evaluates to the largest integral floating-point value less than or equal + to +\series default +\emph on +x +\emph default +. \end_layout \begin_layout Description @@ -6520,7 +6702,7 @@ int \series default int \series medium -(string +(string|float \emph on \begin_inset space ~ @@ -6528,14 +6710,67 @@ int s \emph default -) +[, +\emph on + +\begin_inset space ~ +\end_inset + +rounding +\emph default +]) \series default converts \emph on s \emph default to an integer number. + In case of a floating point argument, there is an optional second argument + which determines which nearby integer will be selected. + It must be one of the function names +\begin_inset Quotes eld +\end_inset + +ceil, +\begin_inset Quotes erd +\end_inset + +\begin_inset Quotes eld +\end_inset + +floor, +\begin_inset Quotes erd +\end_inset + + +\begin_inset Quotes eld +\end_inset + +round, +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +trunc, +\begin_inset Quotes erd +\end_inset + + and the integer corresponding to the value that function would select is + returned. + (See the documentation of each of these functions.) If the second argument + is omitted, the +\begin_inset Quotes eld +\end_inset + +trunc +\begin_inset Quotes erd +\end_inset + + value is returned. \end_layout \begin_layout Description @@ -6586,7 +6821,11 @@ log x \emph default -[,float|int +[, +\begin_inset space ~ +\end_inset + +float|int \emph on \begin_inset space ~ @@ -6594,7 +6833,7 @@ x y \emph default -) +]) \series default evaluates to the logarithm of \emph on @@ -6605,8 +6844,8 @@ x \emph on y \emph default - is specified, the result will be the natural logarithm, otherwise it will - be the logarithm to the base of + is not specified, the result will be the natural logarithm, otherwise it + will be the logarithm to the base of \emph on y \emph default @@ -6800,7 +7039,7 @@ int \series default n \series medium -(location +([location \emph on \begin_inset space ~ @@ -6808,7 +7047,7 @@ n l1 \emph default -, +[, \begin_inset space ~ \end_inset @@ -6820,19 +7059,87 @@ location l2 \emph default -) -\series default - evaluates to the number of non-empty cells in the block marked by the corners - pointed to by +]) \emph on -l1 + +\begin_inset space ~ +\end_inset + + +\emph default +| +\emph on + +\begin_inset space ~ +\end_inset + + +\series default +\emph default +n +\series medium +( +\family roman +\emph on +v1, +\family default + +\begin_inset space ~ +\end_inset + +v2, +\begin_inset space ~ +\end_inset + +... +\emph default +) +\series default + The first form evaluates to the number of non-empty cells in the block + with corners at location +\emph on +s l1 \emph default and \emph on l2 \emph default . - + Location +\emph on +l2 +\emph default + defaults to +\emph on +l1 +\emph default +; i.e., with a single location argument +\family sans +\series bold +n +\family default +\series medium +( +\family sans +\series default +\emph on +l1 +\family default +\series medium +\emph default +) +\series default + just tests whether the cell at +\emph on +l1 +\emph default + is empty. + Location +\emph on +l1 +\emph default + defaults to the current location. + The second form simply returns the number of its arguments which are nonempty. \end_layout \begin_layout Description @@ -6864,7 +7171,7 @@ cn ...]) \series default - evaluates the polynome + evaluates the polynomial \emph on \begin_inset Formula $c_{n}\cdot x^{n}+\ldots+c_{0}\cdot x^{0}$ @@ -6918,8 +7225,46 @@ rnd \series medium () \series default - evaluates to a pseudo-random number between 0.0 and 1.0. - + evaluates to a pseudo-random number between 0.0 and 1.0, changing each time + the expression is evaluated. +\end_layout + +\begin_layout Description + +\series medium +float +\begin_inset space ~ +\end_inset + + +\series default +round +\series medium +(float +\emph on + +\begin_inset space ~ +\end_inset + +x +\emph default +) evaluates to the argument +\series default +\emph on +x +\emph default + rounded to an integral value. + Unless the system floating-point rounding direction has been changed, this + will be the integral value nearest to +\emph on +x +\emph default +. +\end_layout + +\begin_layout Description +scientific Used as a keyword argument to the string() function; listed here + to record that this identifier may not be used as a cell label. \end_layout \begin_layout Description @@ -6990,6 +7335,34 @@ x \begin_layout Description +\series medium +float +\begin_inset space ~ +\end_inset + + +\series default +sqrt +\series medium +(float|int +\emph on + +\begin_inset space ~ +\end_inset + +x +\emph default +) +\series default + evaluates to the square root of +\emph on +x +\emph default +. +\end_layout + +\begin_layout Description + \series medium string \begin_inset space ~ @@ -7065,58 +7438,8 @@ string \series default string \series medium -(location +( \emph on - -\begin_inset space ~ -\end_inset - -l -\emph default -) -\end_layout - -\begin_layout Description - -\series medium -string -\begin_inset space ~ -\end_inset - - -\series default -string -\series medium -(integer -\emph on - -\begin_inset space ~ -\end_inset - -x -\emph default -) -\series default - -\end_layout - -\begin_layout Description - -\series medium -string -\begin_inset space ~ -\end_inset - - -\series default -string -\series medium -(float -\emph on - -\begin_inset space ~ -\end_inset - x \emph default [, @@ -7143,28 +7466,30 @@ integer scientific \emph default -]]) +]]) \series default - evaluates to a string containing the current value of the given cell at - location -\emph on -l -\emph default -, or to the numeric value -\emph on -x -\emph default - with the given -\emph on -precision -\emph default -. - The -\emph on + evaluates to the string representation of its first argument. + The optional second argument gives the precision used for converting floating + point numbers to string form. + The optional third argument may either be the keyword +\begin_inset Quotes eld +\end_inset + scientific -\emph default - flag determines if decimal (0) or scientific (unequal 0) representation - is used. +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +decimal, +\begin_inset Quotes erd +\end_inset + + controlling the type of notation used for converting floating point numbers + to string form. + If the optional arguments are not specified, current defaults are used. \end_layout @@ -7199,7 +7524,7 @@ string datetime \emph default -) +) \series default evaluates to the seconds since epoch (1970-1-1 0:00) of the \emph on @@ -7233,6 +7558,10 @@ substr \end_inset s, +\begin_inset space ~ +\end_inset + + \emph default integer \emph on @@ -7240,9 +7569,13 @@ integer \begin_inset space ~ \end_inset -x, +x +\begin_inset space ~ +\end_inset + + \emph default -integer +[,integer \emph on \begin_inset space ~ @@ -7250,7 +7583,7 @@ integer y \emph default -) +]) \series default evaluates to the substring of \emph on @@ -7265,7 +7598,7 @@ x y \emph default , which start at 0. - + If is omitted, the substring proceeds to the end of the string. \end_layout \begin_layout Description @@ -7291,10 +7624,52 @@ location l2 \emph default -) +) +\emph on + +\begin_inset space ~ +\end_inset + + +\emph default +| +\emph on + +\begin_inset space ~ +\end_inset + + \series default -evaluates to the sum of all values in the block marked by the corners pointed - to by +\emph default +sum +\series medium +( +\family roman +\emph on +v1, +\family default + +\begin_inset space ~ +\end_inset + + +\family roman +v2, +\family default + +\begin_inset space ~ +\end_inset + + +\family roman +... +\emph default +) +\family default + +\series default +The first form evaluates to the sum of all values in the block with corners + at locations \emph on l1 \emph default @@ -7303,6 +7678,7 @@ l1 l2 \emph default . + The second form simply adds all of its arguments. \end_layout @@ -7369,7 +7745,93 @@ x x \emph default is given in radians. - +\end_layout + +\begin_layout Description + +\series medium +float +\begin_inset space ~ +\end_inset + + +\series default +tau +\series medium +[()] +\series default +evaluates to the circle constant +\begin_inset Formula $\tau$ +\end_inset + +, the ratio of circumference to radius of any circle. + (Note +\begin_inset Formula $\tau=2\pi$ +\end_inset + +.) +\end_layout + +\begin_layout Description + +\series medium +int +\begin_inset space ~ +\end_inset + + +\series default +time +\series medium +[()] +\begin_inset ERT +status open + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\series default + each time this expression is evaluated, it gives the time in seconds since + the epoch. +\end_layout + +\begin_layout Description + +\series medium +float +\begin_inset space ~ +\end_inset + + +\series default +trunc +\series medium +(float +\emph on + +\begin_inset space ~ +\end_inset + +x +\emph default +) evaluates to the integer part of +\series default +\emph on +x +\emph default + in floating-point form; generally this should be the same as +\emph on +x - +\emph default +frac( +\emph on +x +\emph default +). \end_layout \begin_layout Description @@ -7456,7 +7918,7 @@ z position of the given location, of the currently updated cell if none is given. These functions are usually used in combination with the @ function for - relative relations to other cell, but see also the convenience functions + obtaining information from other cells, but see also the convenience functions \family sans \series bold @@ -7703,7 +8165,7 @@ function::= \emph on identifier \emph default - + [ \family typewriter ( \family default @@ -7722,6 +8184,8 @@ term ] } \family typewriter ) +\family default +] \end_layout \begin_layout Description @@ -7868,6 +8332,56 @@ factor } \end_layout +\begin_layout Subsection +Implementation notes +\end_layout + +\begin_layout Standard +Note that the implementation of a function can tell the difference between + whether it is called with parentheses and no arguments, like +\begin_inset Quotes eld +\end_inset + +func() +\begin_inset Quotes erd +\end_inset + +, or called without parentheses at all, like +\begin_inset Quotes eld +\end_inset + +func +\begin_inset Quotes erd +\end_inset + + – in the former case it receives an argument count of 0, and in the latter + an argument count of -1. + So the same symbol can have different semantics depending on which way + it is called, but in most cases that would probably be counterintuitive. + The no-parentheses form is useful, however, for constants like +\begin_inset Quotes eld +\end_inset + +e, +\begin_inset Quotes erd +\end_inset + + and it is also used to allow, for example, the names of the functions +\begin_inset Quotes eld +\end_inset + +floor, +\begin_inset Quotes eld +\end_inset + +round, +\begin_inset Quotes erd +\end_inset + + etc. + to be used as keywords for the int() function. +\end_layout + \begin_layout Section Frequently Asked Questions \end_layout @@ -7910,7 +8424,7 @@ This is not a bug in teapot, in fact it is not a bug at all. \begin_layout Standard To solve the comparison problem, teapot has the operator \family typewriter -= +~= \family default (in contrast to the operator ==), which compares two floating point values apart from the last significant bit. @@ -8070,7 +8584,10 @@ referring to what you want. \begin_layout Standard To provide for this need, teapot has a function \family sans -X(SRC, REF) +\series bold +X +\series default +(SRC, REF) \family default to retrieve the value of the cell labeled \family sans @@ -8132,7 +8649,7 @@ SRC \end_layout \begin_layout Standard -Sometimes you want to make this kind of reference but fix one of the coordinates +Sometimes you want to make this kind of reference and fix one of the coordinates but not the others; \family sans X() diff --git a/src/common/cell.c b/src/common/cell.c index 57bf9a1..a435624 100644 --- a/src/common/cell.c +++ b/src/common/cell.c @@ -7,39 +7,52 @@ #include "eval.h" #include "main.h" +const char *ColorAspect_Name[] = + { [FOREGROUND] = "Foreground", [BACKGROUND] = "Background" + }; const ColorNum DefaultCN[] = { [FOREGROUND] = 0, [BACKGROUND] = 16, [NUM_COLOR_ASPECTS] = 255 }; +const char *TokVariety_Name[] = + { [BASE_CONT] = "Base Content", [ITER_CONT] = "Iterative Content", + [ATTR_REF] = "Attribute Reference", [CURR_VAL] = "Current Value", + [RES_VAL] = "Resultant Value", [CONTINGENT] = "Contingent Content" + }; + /* initcellcontents - make a fresh cell into the "empty" one; don't worry about freeing anything there, that will have been handled. */ void initcellcontents(Cell *fresh) { - (void)memset(fresh->contents, 0, sizeof(fresh->contents)); - fresh->label=(char*)0; - fresh->adjust=AUTOADJUST; - fresh->precision=-1; - fresh->shadowed=0; - fresh->bold=0; - fresh->underline=0; + for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv) + fresh->tok[tv].type = EMPTY; + fresh->label = NULL; + fresh->precision = -1; + fresh->adjust = AUTOADJUST; for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a) fresh->aspect[a] = DefaultCN[a]; - fresh->scientific=DEF_SCIENTIFIC; - fresh->value.type=EMPTY; - fresh->resvalue.type=EMPTY; - fresh->locked=0; - fresh->ignored=0; - fresh->clock_t0=0; - fresh->clock_t1=0; - fresh->clock_t2=0; + fresh->updated = 0; + fresh->shadowed = 0; + fresh->scientific = DEF_SCIENTIFIC; + fresh->locked = 0; + fresh->transparent = 0; + fresh->ignored = 0; + fresh->clock_t0 = 0; + fresh->clock_t1 = 0; + fresh->clock_t2 = 0; + fresh->bold = 0; + fresh->underline = 0; } /* getcont -- get contents */ -Token **getcont(const Cell *cell, ContentVariety v) +Token gettok(const Cell *cell, TokVariety v) { - if (cell == NULLCELL) return EMPTY_TVEC; + Token emp; + emp.type = EMPTY; + if (cell == NULLCELL) return emp; if (v == CONTINGENT) - v = (cell->clock_t0 && cell->contents[ITERATIVE]) ? ITERATIVE : BASE; - return cell->contents[v]; + v = (cell->clock_t0 && cell->tok[ITER_CONT].type != EMPTY) + ? ITER_CONT : BASE_CONT; + return cell->tok[v]; } /* getadjust -- get cell adjustment */ @@ -47,7 +60,8 @@ Adjust getadjust(const Cell* cell) { if (cell == NULLCELL) return LEFT; else if (cell->adjust == AUTOADJUST) - return (cell->value.type == INT || cell->value.type == FLOAT ? RIGHT : LEFT); + return (cell->tok[CURR_VAL].type == INT + || cell->tok[CURR_VAL].type == FLOAT) ? RIGHT : LEFT; else return cell->adjust; } @@ -148,17 +162,13 @@ static void copytokens(Token*** totoks, Token** fromtoks) /* freecellcontents -- free the resources of the cell at destruction time */ void freecellcontents(Cell *faded) { - tvecfree(faded->contents[BASE]); - tvecfree(faded->contents[ITERATIVE]); - tfree(&(faded->value)); - tfree(&(faded->resvalue)); - if (faded->label != (char*)0) { - free(faded->label); - } + for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv) + tfree(&(faded->tok[tv])); + if (faded->label != NULL) free(faded->label); } /* copycell - copies one Cell to another, handling any allocation issues */ -void copycell(Cell *to, const Cell *fromcell) +void copycell(Cell *to, const Cell *fromcell, LabelHandling lh) { assert(to != NULLCELL); if (to == fromcell) return; @@ -166,16 +176,15 @@ void copycell(Cell *to, const Cell *fromcell) freecellcontents(to); if (fromcell != NULLCELL) { memcpy(to, fromcell, sizeof(Cell)); - copytokens(&(to->contents[BASE]), fromcell->contents[BASE]); - copytokens(&(to->contents[ITERATIVE]), fromcell->contents[ITERATIVE]); - if (fromcell->label != (char*)0) { + for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv) + if (tv <= MAX_PERSIST_TV) to->tok[tv] = tcopy(fromcell->tok[tv]); + else to->tok[tv].type = EMPTY; + if (lh != PRESERVE_LABEL && fromcell->label != (char*)0) { size_t len = strlen(fromcell->label); to->label = strcpy(malloc(len+2), fromcell->label); (to->label)[len] = '_'; (to->label)[len+1] = '\0'; } - to->value.type = EMPTY; - to->resvalue.type = EMPTY; } else { initcellcontents(to); } diff --git a/src/common/cell.h b/src/common/cell.h index 85cfc28..4fec95a 100644 --- a/src/common/cell.h +++ b/src/common/cell.h @@ -9,23 +9,26 @@ extern "C" { typedef enum { LEFT=0, RIGHT=1, CENTER=2, AUTOADJUST=3 } Adjust; -typedef enum { BASE=0, ITERATIVE=1, CONTINGENT=2 } ContentVariety; +typedef enum + { BASE_CONT=0, ITER_CONT, ATTR_REF, MAX_PERSIST_TV = ATTR_REF, + CURR_VAL, RES_VAL, + CONTINGENT /* Must be last, marks end of many loops */ + } TokVariety; +extern const char *TokVariety_Name[]; typedef unsigned char ColorNum; - #define MAX_MAX_COLORS UCHAR_MAX; typedef enum { FOREGROUND = 0, BACKGROUND, NUM_COLOR_ASPECTS } ColorAspect; +extern const char *ColorAspect_Name[]; extern const ColorNum DefaultCN[]; typedef struct { - Token **contents[CONTINGENT]; + Token tok[CONTINGENT]; char *label; - Token value; - Token resvalue; - Adjust adjust; int precision; + Adjust adjust; ColorNum aspect[NUM_COLOR_ASPECTS]; unsigned int updated:1; unsigned int shadowed:1; @@ -42,7 +45,9 @@ typedef struct #define NULLCELL ((Cell*)0) -Token **getcont(const Cell *cell, ContentVariety v); +typedef enum {ALTER_LABEL, PRESERVE_LABEL} LabelHandling; + +Token gettok(const Cell *cell, TokVariety v); Adjust getadjust(const Cell *cell); bool shadowed(const Cell *cell); bool isbold(const Cell *cell); @@ -56,7 +61,7 @@ int getprecision(const Cell* cell); const char *getlabel(const Cell *cell); void initcellcontents(Cell *cell); void freecellcontents(Cell *cell); -void copycell(Cell *to, const Cell *from); + void copycell(Cell *to, const Cell *from, LabelHandling lh); #ifdef __cplusplus } diff --git a/src/common/csv.c b/src/common/csv.c index e35fd5a..855f9ee 100644 --- a/src/common/csv.c +++ b/src/common/csv.c @@ -17,6 +17,7 @@ #endif #include "csv.h" +#include "scanner.h" static int semicol=0; @@ -77,15 +78,15 @@ void csv_separator(const char *s, const char **end) } /*}}}*/ /* csv_long -- convert string [0[x]]12345 to long */ /*{{{*/ -long csv_long(const char *s, const char **end) +IntT csv_long(const char *s, const char **end) { - long value; + IntT value; const char *t; assert(s!=(const char*)0); assert(end!=(const char**)0); if (*s=='\t') { *end=s; return 0L; }; - value=strtol(s,(char**)end,0); + value = STRTOINT(s, (char**)end, 0); if (s!=*end) { t=*end; csv_separator(t,end); @@ -95,15 +96,15 @@ long csv_long(const char *s, const char **end) } /*}}}*/ /* csv_double -- convert string 123.4e5 to double */ /*{{{*/ -double csv_double(const char *s, const char **end) +FltT csv_double(const char *s, const char **end) { - double value; + FltT value; const char *t; assert(s!=(const char*)0); assert(end!=(const char**)0); if (*s=='\t') { *end=s; return 0.0; }; - value=strtod(s,(char**)end); + value = STRTOFLT(s,(char**)end); if (s!=*end) { t=*end; csv_separator(t,end); @@ -112,6 +113,7 @@ double csv_double(const char *s, const char **end) *end=s; return 0.0; } /*}}}*/ + /* csv_string -- convert almost any string to string */ /*{{{*/ char *csv_string(const char *s, const char **end) { diff --git a/src/common/csv.h b/src/common/csv.h index cf40e17..c20badf 100644 --- a/src/common/csv.h +++ b/src/common/csv.h @@ -1,6 +1,8 @@ #ifndef CSV_H #define CSV_H +#include "scanner.h" + struct CSV_Date { int day; @@ -11,7 +13,7 @@ struct CSV_Date void csv_setopt(int sem); void csv_separator(const char *s, const char **end); char *csv_string(const char *s, const char **end); -double csv_double(const char *s, const char **end); -long csv_long(const char *s, const char **end); +FltT csv_double(const char *s, const char **end); +IntT csv_long(const char *s, const char **end); #endif diff --git a/src/common/eval.c b/src/common/eval.c index b324d58..2a65935 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -48,7 +48,7 @@ Token tcopy(Token n) result.u.funcall.argv = malloc(n.u.funcall.argc*sizeof(Token)); for (size_t ai = 0; ai < n.u.funcall.argc; ++ai) result.u.funcall.argv[ai] = tcopy(n.u.funcall.argv[ai]); - } + } else result.u.funcall.argv = NULLTOKEN; break; case EEK: result.u.err = strdup(n.u.err); break; default: break; @@ -106,6 +106,7 @@ void tfree_protected(Token *n, const Token dontfree) default: break; } + n->type = EMPTY; } /*}}}*/ @@ -141,104 +142,94 @@ size_t tveclen(Token **tvec) } /*}}}*/ +/* checkflt -- check for error conditions in floating point result */ +static Token checkflt(Token res, const char* context) +{ + if (res.type != FLOAT) return res; + const char *msg = dblfinite(res.u.flt); + if (msg == NULL) return res; + res.type = EEK; + res.u.err = malloc(strlen(msg) + strlen(context) + 1); + (void)strcpy(res.u.err, context); + (void)strcat(res.u.err, msg); + return res; +} +/* operand_type_error -- return an error message about operands */ +static Token operand_type_error(const char* context, Type lt, Type rt) +{ + const char* templ = _("wrong operand types, %s and %s"); + size_t conlen = strlen(context); + Token err; + + err.type = EEK; + err.u.err = malloc(conlen + strlen(templ) + 2*MAX_TYPE_NAME_LENGTH + 1); + strcpy(err.u.err, context); + sprintf(err.u.err + conlen, templ, Type_Name[lt], Type_Name[rt]); + return err; +} + /* tadd -- + operator */ /*{{{*/ Token tadd(Token l, Token r) { /* variables */ /*{{{*/ Token result; - const char *msg; - char *buf; - int len; + const char *cntxt = "+: "; /*}}}*/ - if (l.type==EEK) - /* return left error */ /*{{{*/ - return tcopy(l); - /*}}}*/ - else if (r.type==EEK) - /* return right error */ /*{{{*/ - return tcopy(r); - /*}}}*/ - else if (l.type==INT && r.type==INT) + if (l.type == EEK || r.type == EMPTY) return tcopy(l); + if (r.type == EEK || l.type == EMPTY) return tcopy(r); + if (l.type == INT && r.type == INT) /* result is int sum of two ints */ /*{{{*/ { - result.type=INT; - result.u.integer=l.u.integer+r.u.integer; + result.type = INT; + result.u.integer = l.u.integer+r.u.integer; + return result; } /*}}}*/ - else if (l.type==STRING && r.type==STRING) + if (l.type == STRING && r.type == STRING) /* result is concatenated strings */ /*{{{*/ { - result.type=STRING; - result.u.string=malloc(strlen(l.u.string)+strlen(r.u.string)+1); - (void)strcpy(result.u.string,l.u.string); - (void)strcat(result.u.string,r.u.string); + result.type = STRING; + result.u.string = malloc(strlen(l.u.string) + strlen(r.u.string) + 1); + (void)strcpy(result.u.string, l.u.string); + (void)strcat(result.u.string, r.u.string); + return result; } /*}}}*/ - else if (l.type==EMPTY && (r.type==INT || r.type==STRING || r.type==FLOAT || r.type==EMPTY)) - /* return right argument */ /*{{{*/ - return tcopy(r); - /*}}}*/ - else if ((l.type==INT || l.type==STRING || l.type==FLOAT) && r.type==EMPTY) - /* return left argument */ /*{{{*/ - return tcopy(l); - /*}}}*/ - else if (l.type==INT && r.type==FLOAT) + if (l.type == INT && r.type == FLOAT) /* result is float sum of int and float */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=((double)l.u.integer)+r.u.flt; + result.type = FLOAT; + result.u.flt = ((FltT)l.u.integer) + r.u.flt; + return checkflt(result, cntxt); } /*}}}*/ - else if (l.type==FLOAT && r.type==INT) + if (l.type == FLOAT && r.type == INT) /* result is float sum of float and int */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=l.u.flt+((double)r.u.integer); + result.type = FLOAT; + result.u.flt = l.u.flt + ((FltT)r.u.integer); + return checkflt(result, cntxt); } /*}}}*/ - else if (l.type==FLOAT && r.type==FLOAT) + if (l.type == FLOAT && r.type == FLOAT) /* result is float sum of float and float */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=l.u.flt+r.u.flt; + result.type = FLOAT; + result.u.flt = l.u.flt + r.u.flt; + return checkflt(result, cntxt); } /*}}}*/ - else if (l.type == LOCATION && r.type == LOCATION) + if (l.type == LOCATION && r.type == LOCATION) /* result is component-wise sum of locations */ /*{{{*/ { result.type = LOCATION; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.location[len] = l.u.location[len] + r.u.location[len]; + return result; } /*}}}*/ - else if (l.type==EMPTY && r.type==EMPTY) - /* result is emty */ /*{{{*/ - { - result.type=EMPTY; - } - /*}}}*/ - else - /* result is type error */ /*{{{*/ - { - result.type=EEK; - len = strlen(_("wrong types for + operator")); - buf = malloc(len + 5 + 2*MAX_TYPE_NAME_LENGTH + 1); - strcpy(buf, _("wrong types for + operator")); - snprintf(buf + len, 128, ": %s + %s", Type_Name[l.type], Type_Name[r.type]); - result.u.err = buf; - } - /*}}}*/ - if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) - /* result is error */ /*{{{*/ - { - result.type=EEK; - result.u.err=malloc(strlen(msg)+4); - (void)strcpy(result.u.err,"+: "); - (void)strcat(result.u.err,msg); - } - /*}}}*/ - return result; + return operand_type_error(cntxt, l.type, r.type); } /*}}}*/ @@ -253,8 +244,8 @@ Token tconcat(Token l, Token r) int len; /*}}}*/ - if (l.type==EEK) return tcopy(l); - if (r.type==EEK) return tcopy(r); + if (l.type == EEK) return tcopy(l); + if (r.type == EEK) return tcopy(r); len = printtok(buf, conc_buf_len, 0, 0, DEF_SCIENTIFIC, def_precision, 0, &l); if (len > conc_buf_len - 2) { @@ -280,23 +271,17 @@ Token tsub(Token l, Token r) { /* variables */ /*{{{*/ Token result; - const char *msg; - int len; + const char *cntxt = "-: "; /*}}}*/ - if (l.type==EEK) - /* return left error */ /*{{{*/ - return tcopy(l); - /*}}}*/ - else if (r.type==EEK) - /* return right error */ /*{{{*/ - return tcopy(r); - /*}}}*/ - else if (l.type==INT && r.type==INT) + if (l.type == EEK || r.type == EMPTY) return tcopy(l); + if (r.type == EEK) return tcopy(r); + if (l.type == EMPTY) return tneg(r); + if (l.type == INT && r.type == INT) /* result is int difference between left int and right int */ /*{{{*/ { - result.type=INT; - result.u.integer=l.u.integer-r.u.integer; + result.type = INT; + result.u.integer = l.u.integer - r.u.integer; } /*}}}*/ else if (l.type==FLOAT && r.type==FLOAT) @@ -306,210 +291,153 @@ Token tsub(Token l, Token r) result.u.flt=l.u.flt-r.u.flt; } /*}}}*/ - else if (l.type==EMPTY) - /* return negated right argument */ /*{{{*/ - return tneg(r); - /*}}}*/ - else if ((l.type==INT || l.type==FLOAT) && r.type==EMPTY) - /* return left argument */ /*{{{*/ - return tcopy(l); - /*}}}*/ else if (l.type==INT && r.type==FLOAT) /* result is float difference of left integer and right float */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=((double)l.u.integer)-r.u.flt; + result.type = FLOAT; + result.u.flt = ((FltT)l.u.integer) - r.u.flt; } /*}}}*/ else if (l.type==FLOAT && r.type==INT) /* result is float difference between left float and right integer */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=l.u.flt-((double)r.u.integer); + result.type = FLOAT; + result.u.flt = l.u.flt - ((FltT)r.u.integer); } /*}}}*/ else if (l.type == LOCATION && r.type == LOCATION) /* result is component-wise difference of locations */ /*{{{*/ { result.type = LOCATION; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.location[len] = l.u.location[len] - r.u.location[len]; } /*}}}*/ - else - /* result is difference type error */ /*{{{*/ - { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("wrong types for - operator"))+1),_("wrong types for - operator")); - } - /*}}}*/ - if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) - /* result is error */ /*{{{*/ - { - result.type=EEK; - result.u.err=malloc(strlen(msg)+4); - (void)strcpy(result.u.err,"-: "); - (void)strcat(result.u.err,msg); - } - /*}}}*/ - return result; + else return operand_type_error(cntxt, l.type, r.type); + return checkflt(result, cntxt); } /*}}}*/ + /* tdiv -- / operator */ /*{{{*/ Token tdiv(Token l, Token r) { /* variables */ /*{{{*/ Token result; - const char *msg; - int len; + const char *cntxt = "/: "; /*}}}*/ - if (l.type==EEK) - /* return left error */ /*{{{*/ - return tcopy(l); - /*}}}*/ - else if (r.type==EEK) - /* return right error */ /*{{{*/ - return tcopy(r); - /*}}}*/ - else if ((r.type==INT && r.u.integer==0) || (r.type==FLOAT && r.u.flt==0.0) || (r.type==EMPTY)) + if (l.type == EEK) return tcopy(l); + if (r.type == EEK) return tcopy(r); + if ((r.type == INT && r.u.integer == 0)|| (r.type == FLOAT && r.u.flt == 0.0) + || (r.type == EMPTY)) /* result is division by 0 error */ /*{{{*/ { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("division by 0"))+1),_("division by 0")); + duperror(&result, _("division by 0")); + return result; } /*}}}*/ - else if (l.type==INT && r.type==INT) + if (l.type == EMPTY && TOKISNUM(r)) + if (r.type == INT) + { + result.type = INT; result.u.integer = 0; return result; + } else { + result.type = FLOAT; result.u.flt = 0.0; return result; + } + if (l.type==INT && r.type==INT) /* result is quotient of left int and right int */ /*{{{*/ { - result.type=INT; - result.u.integer=l.u.integer/r.u.integer; + result.type = INT; + result.u.integer = l.u.integer/r.u.integer; + return result; } /*}}}*/ - else if (l.type==FLOAT && r.type==FLOAT) + if (l.type == FLOAT && r.type == FLOAT) /* result is quotient of left float and right float */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=l.u.flt/r.u.flt; + result.type = FLOAT; + result.u.flt = l.u.flt/r.u.flt; } /*}}}*/ - else if (l.type==EMPTY && r.type==INT) - /* result is 0 */ /*{{{*/ - { - result.type=INT; - result.u.integer=0; - } - /*}}}*/ - else if (l.type==EMPTY && r.type==FLOAT) - /* result is 0.0 */ /*{{{*/ - { - result.type=FLOAT; - result.u.flt=0.0; - } - /*}}}*/ - else if (l.type==INT && r.type==FLOAT) + else if (l.type == INT && r.type == FLOAT) /* result is float quotient of left int and right float */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=((double)l.u.integer)/r.u.flt; + result.type = FLOAT; + result.u.flt = ((FltT)l.u.integer)/r.u.flt; } /*}}}*/ - else if (l.type==FLOAT && r.type==INT) + else if (l.type == FLOAT && r.type == INT) /* result is float quotient of left float and right int */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=l.u.flt/((double)r.u.integer); + result.type = FLOAT; + result.u.flt = l.u.flt/((FltT)r.u.integer); } /*}}}*/ else if (l.type == LOCATION && r.type == INT) /* result is divide each component of location by right */ /*{{{*/ { result.type = LOCATION; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.location[len] = l.u.location[len] / r.u.integer; } /*}}}*/ - else - /* result is quotient type error */ /*{{{*/ - { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("wrong types for / operator"))+1),_("wrong types for / operator")); - } - /*}}}*/ - if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) - /* result is error */ /*{{{*/ - { - result.type=EEK; - result.u.err=malloc(strlen(msg)+4); - (void)strcpy(result.u.err,"/: "); - (void)strcat(result.u.err,msg); - } - /*}}}*/ - return result; + else return operand_type_error(cntxt, l.type, r.type); + return checkflt(result, cntxt); } /*}}}*/ + /* tmod -- % operator */ /*{{{*/ Token tmod(Token l, Token r) { /* variables */ /*{{{*/ Token result; - const char *msg; - int len; + const char *cntxt = "%: "; /*}}}*/ - if (l.type==EEK) /* return left error */ /*{{{*/ - return tcopy(l); - /*}}}*/ - else if (r.type==EEK) /* return right error */ /*{{{*/ - return tcopy(r); - /*}}}*/ - else if ((r.type==INT && r.u.integer==0) || (r.type==FLOAT && r.u.flt==0.0) || (r.type==EMPTY)) /* result is modulo 0 error */ /*{{{*/ + if (l.type == EEK) return tcopy(l); + if (r.type == EEK) return tcopy(r); + if ((r.type == INT && r.u.integer == 0) || (r.type == FLOAT && r.u.flt == 0.0) + || (r.type == EMPTY)) /* result is modulo 0 error */ /*{{{*/ { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("modulo 0"))+1),_("modulo 0")); + duperror(&result, _("modulo 0")); + return result; + } + if (l.type == EMPTY && TOKISNUM(r)) + if (r.type == INT) + { + result.type = INT; result.u.integer = 0; return result; + } else { + result.type = FLOAT; result.u.flt = 0.0; return result; + } + if (l.type == INT && r.type == INT) /* result is remainder of left int and right int */ /*{{{*/ + { + result.type = INT; + result.u.integer = l.u.integer % r.u.integer; + return result; } /*}}}*/ - else if (l.type==INT && r.type==INT) /* result is remainder of left int and right int */ /*{{{*/ + if (l.type == FLOAT && r.type == FLOAT) /* result is remainder of left float and right float */ /*{{{*/ { - result.type=INT; - result.u.integer=l.u.integer%r.u.integer; + result.type = FLOAT; + result.u.flt = FMODFLT(l.u.flt, r.u.flt); } /*}}}*/ - else if (l.type==FLOAT && r.type==FLOAT) /* result is remainder of left float and right float */ /*{{{*/ + else if (l.type == INT && r.type == FLOAT) /* result is float remainder of left int and right float */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=fmod(l.u.flt,r.u.flt); + result.type = FLOAT; + result.u.flt = FMODFLT((FltT)l.u.integer, r.u.flt); } /*}}}*/ - else if (l.type==EMPTY && r.type==INT) /* result is 0 */ /*{{{*/ + else if (l.type == FLOAT && r.type == INT) /* result is float remainder of left float and right int */ /*{{{*/ { - result.type=INT; - result.u.integer=0; - } - /*}}}*/ - else if (l.type==EMPTY && r.type==FLOAT) /* result is 0.0 */ /*{{{*/ - { - result.type=FLOAT; - result.u.flt=0.0; - } - /*}}}*/ - else if (l.type==INT && r.type==FLOAT) /* result is float remainder of left int and right float */ /*{{{*/ - { - result.type=FLOAT; - result.u.flt=fmod((double)l.u.integer,r.u.flt); - } - /*}}}*/ - else if (l.type==FLOAT && r.type==INT) /* result is float remainder of left float and right int */ /*{{{*/ - { - result.type=FLOAT; - result.u.flt=fmod(l.u.flt,(double)r.u.integer); + result.type = FLOAT; + result.u.flt = FMODFLT(l.u.flt, (FltT)r.u.integer); } /*}}}*/ else if (l.type == LOCATION && r.type == LOCATION) /* result is component-wise mod of locations */ /*{{{*/ { result.type = LOCATION; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.location[len] = l.u.location[len] % r.u.location[len]; } /*}}}*/ @@ -517,88 +445,78 @@ Token tmod(Token l, Token r) /* result is mod each component of location by right */ /*{{{*/ { result.type = LOCATION; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.location[len] = l.u.location[len] % r.u.integer; } /*}}}*/ - else /* result is remainder type error */ /*{{{*/ - { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("wrong types for % operator"))+1),_("wrong types for % operator")); - } - /*}}}*/ - if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) /* result is error */ /*{{{*/ - { - result.type=EEK; - result.u.err=malloc(strlen(msg)+4); - (void)strcpy(result.u.err,"%: "); - (void)strcat(result.u.err,msg); - } - /*}}}*/ - return result; + else return operand_type_error(cntxt, l.type, r.type); + return checkflt(result, cntxt); } /*}}}*/ + /* tmul -- * operator */ /*{{{*/ Token tmul(Token l, Token r) { /* variables */ /*{{{*/ Token result; - const char *msg; - int len; + const char *cntxt = "*: "; /*}}}*/ - if (l.type == EEK) result = tcopy(l); - else if (r.type == EEK) result = tcopy(r); - else if (l.type == INT && r.type == INT) - /* result is int product of left int and right int */ /*{{{*/ + if (l.type == EEK) return tcopy(l); + if (r.type == EEK) return tcopy(r); + if (l.type==EMPTY && r.type==EMPTY) + /* result is empty */ /*{{{*/ { - result = l; - result.u.integer = l.u.integer*r.u.integer; + result.type = EMPTY; + return result; } /*}}}*/ - else if (l.type == FLOAT && r.type == FLOAT) - /* result is float product of left float and right float */ /*{{{*/ - { - result.type = FLOAT; - result.u.flt = l.u.flt*r.u.flt; - } - /*}}}*/ - else if ((l.type == EMPTY && r.type == INT) - || (l.type == INT && r.type == EMPTY)) + if ((l.type == EMPTY && r.type == INT) || (l.type == INT && r.type == EMPTY)) /* result is 0 */ /*{{{*/ { result.type = INT; result.u.integer = 0; + return result; } /*}}}*/ - else if ((l.type == EMPTY && r.type == FLOAT) - || (l.type == FLOAT && r.type == EMPTY)) + if ((l.type == EMPTY && r.type == FLOAT) + || (l.type == FLOAT && r.type == EMPTY)) /* result is 0.0 */ /*{{{*/ { result.type = FLOAT; result.u.flt = 0.0; + return result; + } + /*}}}*/ + if (l.type == INT && r.type == INT) + /* result is int product of left int and right int */ /*{{{*/ + { + result = l; + result.u.integer = l.u.integer * r.u.integer; + return result; + } + /*}}}*/ + if (l.type == FLOAT && r.type == FLOAT) + /* result is float product of left float and right float */ /*{{{*/ + { + result.type = FLOAT; + result.u.flt = l.u.flt * r.u.flt; } /*}}}*/ else if ((l.type == INT && r.type == FLOAT) - || (l.type == FLOAT && r.type==INT)) + || (l.type == FLOAT && r.type == INT)) /* result is float product of int and float */ /*{{{*/ { result.type = FLOAT; - if (l.type == INT) result.u.flt = ((double)l.u.integer) * r.u.flt; - else result.u.flt = l.u.flt * ((double)r.u.integer); - } - /*}}}*/ - else if (l.type==EMPTY && r.type==EMPTY) - /* result is empty */ /*{{{*/ - { - result.type=EMPTY; + if (l.type == INT) result.u.flt = ((FltT)l.u.integer) * r.u.flt; + else result.u.flt = l.u.flt * ((FltT)r.u.integer); } /*}}}*/ else if (l.type == LOCATION && r.type == INT) /* result is each component of location times right */ /*{{{*/ { result.type = LOCATION; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.location[len] = l.u.location[len] * r.u.integer; } /*}}}*/ @@ -607,7 +525,7 @@ Token tmul(Token l, Token r) { result.type = INT; result.u.integer = 0; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.integer += l.u.location[len] * r.u.location[len]; } /*}}}*/ @@ -644,25 +562,11 @@ Token tmul(Token l, Token r) result.u.string[copies*len] = '\0'; } } - else - /* result is product type error */ /*{{{*/ - { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("wrong types for * operator"))+1),_("wrong types for * operator")); - } - /*}}}*/ - if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) - /* result is error */ /*{{{*/ - { - result.type=EEK; - result.u.err=malloc(strlen(msg)+4); - (void)strcpy(result.u.err,"*: "); - (void)strcat(result.u.err,msg); - } - /*}}}*/ - return result; + else return operand_type_error(cntxt, l.type, r.type); + return checkflt(result, cntxt); } /*}}}*/ + /* tneg -- monadic - operator */ /*{{{*/ Token tneg(Token x) { @@ -671,137 +575,107 @@ Token tneg(Token x) int len; /*}}}*/ - if (x.type==EEK) - /* return error */ /*{{{*/ - return tcopy(x); - /*}}}*/ - else if (x.type==INT) + if (x.type == EEK) return tcopy(x); + if (x.type == EMPTY) return x; + if (x.type == INT) /* result is negated int argument */ /*{{{*/ { - result.type=INT; - result.u.integer=-x.u.integer; + result.type = INT; + result.u.integer = -x.u.integer; + return result; } /*}}}*/ - else if (x.type==FLOAT) + if (x.type == FLOAT) /* result is negated float argument */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=-x.u.flt; + result.type = FLOAT; + result.u.flt = -x.u.flt; + return result; } /*}}}*/ - else if (x.type==EMPTY) - /* result is argument itself */ /*{{{*/ - { - result=tcopy(x); - } - /*}}}*/ - else if (x.type == LOCATION) + if (x.type == LOCATION) /* result is component-wise negation of location */ /*{{{*/ { result.type = LOCATION; - for (len = 0; len < 3; ++len) + for (size_t len = 0; len < 3; ++len) result.u.location[len] = -x.u.location[len]; + return result; } - /*}}}*/ - else - /* result is negation error */ /*{{{*/ - { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("wrong type for - operator"))+1),_("wrong type for - operator")); - } - /*}}}*/ + result.type = EEK; + const char* templ = "wrong type for - operator: %s"; + result.u.err = malloc(strlen(templ) + MAX_TYPE_NAME_LENGTH + 1); + sprintf(result.u.err, templ, Type_Name[x.type]); return result; } /*}}}*/ + /* tpow -- ^ operator */ /*{{{*/ Token tpow(Token l, Token r) { /* variables */ /*{{{*/ Token result; - const char *msg; + const char *cntxt = "^: "; /*}}}*/ - if (l.type==EEK) /* return left error */ /*{{{*/ - return tcopy(l); - /*}}}*/ - else if (r.type==EEK) /* return right error */ /*{{{*/ - return tcopy(r); - /*}}}*/ - else if ((l.type==INT || l.type==FLOAT || l.type==EMPTY) && (r.type==INT || r.type==FLOAT || l.type==EMPTY)) /* do the real work */ /*{{{*/ + if (l.type == EEK) return tcopy(l); + if (r.type == EEK) return tcopy(r); + if (TOKISNUM(r) && TOKISNUM(l)) { - if ((l.type==INT || l.type==EMPTY) && ((r.type==INT && r.u.integer>=0) || r.type==EMPTY)) + if ((l.type == INT || l.type == EMPTY) + && ((r.type == INT && r.u.integer >= 0) || r.type == EMPTY)) /* int^int, return int or error if 0^0 */ /*{{{*/ { - long x,y; + IntT x,y; - if (l.type==EMPTY) x=0; - else x=l.u.integer; - if (r.type==EMPTY) y=0; - else y=r.u.integer; - if (x==0 && y==0) - { - result.type=EEK; - result.u.err=strcpy(malloc(strlen(_("0^0 is not defined"))+1),_("0^0 is not defined")); - } + if (l.type == EMPTY) x=0; + else x = l.u.integer; + if (r.type == EMPTY) y=0; + else y = r.u.integer; + if (x == 0 && y == 0) duperror(&result, _("0^0 is not defined")); else { - long i; + UIntT i; - result.type=INT; - if (x==0) result.u.integer=0; - else if (y==0) result.u.integer=1; - else for (result.u.integer=x,i=1; i= operator */ /*{{{*/ Token tge(Token l, Token r) { @@ -1053,16 +928,16 @@ Token tge(Token l, Token r) result.u.integer=l.u.flt>=r.u.flt; } /*}}}*/ - else if (l.type==FLOAT && r.type==INT) /* return left float >= (double) right int */ /*{{{*/ + else if (l.type==FLOAT && r.type==INT) /* return left float >= (FltT) right int */ /*{{{*/ { - result.type=INT; - result.u.integer=l.u.flt>=((double)r.u.integer); + result.type = INT; + result.u.integer = l.u.flt >= ((FltT)r.u.integer); } /*}}}*/ - else if (l.type==INT && r.type==FLOAT) /* return (double) left int >= right float */ /*{{{*/ + else if (l.type==INT && r.type==FLOAT) /* return (FltT) left int >= right float */ /*{{{*/ { - result.type=INT; - result.u.integer=((double)l.u.integer)>=r.u.flt; + result.type = INT; + result.u.integer = ((FltT)l.u.integer) >= r.u.flt; } /*}}}*/ else if (l.type == LOCATION && r.type == LOCATION) @@ -1136,16 +1011,16 @@ Token tgt(Token l, Token r) result.u.integer=l.u.flt>r.u.flt; } /*}}}*/ - else if (l.type==FLOAT && r.type==INT) /* result is left float > (double) right int */ /*{{{*/ + else if (l.type==FLOAT && r.type==INT) /* result is left float > (FltT) right int */ /*{{{*/ { - result.type=INT; - result.u.integer=l.u.flt>((double)r.u.integer); + result.type = INT; + result.u.integer = l.u.flt > ((FltT)r.u.integer); } /*}}}*/ else if (l.type==INT && r.type==FLOAT) /* result is left int > right float */ /*{{{*/ { - result.type=INT; - result.u.integer=((double)l.u.integer)>r.u.flt; + result.type = INT; + result.u.integer = ((FltT)l.u.integer) > r.u.flt; } /*}}}*/ else if (l.type == LOCATION && r.type == LOCATION) @@ -1202,18 +1077,18 @@ Token teq(Token l, Token r) /*}}}*/ if (l.type==FLOAT && r.type==INT) { - result.type=INT; - result.u.integer=l.u.flt==((double)r.u.integer); + result.type = INT; + result.u.integer = (l.u.flt == ((FltT)r.u.integer)); } else if (l.type==INT && r.type==FLOAT) { - result.type=INT; - result.u.integer=((double)l.u.integer)==r.u.flt; + result.type = INT; + result.u.integer = (((FltT)l.u.integer) == r.u.flt); } - else if (l.type!=r.type) + else if (l.type != r.type) { - result.type=INT; - result.u.integer=0; + result.type = INT; + result.u.integer = 0; } else if (l.type==INT) { @@ -1249,6 +1124,22 @@ Token teq(Token l, Token r) } /*}}}*/ +static bool nearly_equal(FltT a1, FltT a2) +{ + if (a1 == 0 && a2 == 0) + return true; + + FltT A1 = ABSFLT(a1); + FltT A2 = ABSFLT(a2); + FltT max = A1 > A2 ? A1 : A2; + FltT eps = FLTEPS; + FltT diff = ABSFLT(a1-a2); + FltT thelog = LOG2FLT(max); + int expn = thelog; + FltT scale = POWFLT(2.0, expn); + return ABSFLT(a1 - a2) <= eps * scale; +} + /* tabouteq -- ~= operator */ /*{{{*/ Token tabouteq(Token l, Token r) { @@ -1257,7 +1148,7 @@ Token tabouteq(Token l, Token r) /*}}}*/ if (l.type==EEK) return tcopy(l); - else if (r.type==EEK) return tcopy(r); + if (r.type==EEK) return tcopy(r); if (l.type==EMPTY) /* try to assign 0 element of r.type */ /*{{{*/ { @@ -1283,7 +1174,7 @@ Token tabouteq(Token l, Token r) if (l.type==FLOAT && r.type==FLOAT) { result.type=INT; - result.u.integer=(fabs(l.u.flt-r.u.flt)<=DBL_EPSILON); + result.u.integer = (int)nearly_equal(l.u.flt, r.u.flt); } else { @@ -1303,7 +1194,7 @@ Token tne(Token l, Token r) /*}}}*/ if (l.type==EEK) return tcopy(l); - else if (r.type==EEK) return tcopy(r); + if (r.type==EEK) return tcopy(r); if (l.type==EMPTY) /* try to assign 0 element of r.type */ /*{{{*/ { @@ -1332,18 +1223,18 @@ Token tne(Token l, Token r) /*}}}*/ if (l.type==FLOAT && r.type==INT) { - result.type=INT; - result.u.integer=l.u.flt!=((double)r.u.integer); + result.type = INT; + result.u.integer = l.u.flt != ((FltT)r.u.integer); } else if (l.type==INT && r.type==FLOAT) { - result.type=INT; - result.u.integer=((double)l.u.integer)!=r.u.flt; + result.type = INT; + result.u.integer = ((FltT)l.u.integer)!=r.u.flt; } - else if (l.type!=r.type) + else if (l.type != r.type) { - result.type=INT; - result.u.integer=1; + result.type = INT; + result.u.integer = 1; } else if (l.type==INT) { diff --git a/src/common/func.c b/src/common/func.c index cb84bce..8e7f304 100644 --- a/src/common/func.c +++ b/src/common/func.c @@ -18,6 +18,7 @@ #include #include extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */ +extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */ #include extern char *strdup(const char* s); #include @@ -38,12 +39,12 @@ extern char *strdup(const char* s); #ifdef M_E #define CONST_E M_E #else -#define CONST_E ((double)2.7182818284590452354) +#define CONST_E ((FltT)2.7182818284590452354) #endif #ifdef M_PI #define CONST_PI M_PI #else -#define CONST_PI ((double)3.14159265358979323846) +#define CONST_PI ((FltT)3.14159265358979323846) #endif /*}}}*/ @@ -230,8 +231,21 @@ char* strptime(const char *buf, const char *fmt, struct tm *tm) { } #endif +/* identcode -- return number of identifier */ /*{{{*/ +FunctionIdentifier identcode(const char *s, size_t len) +{ + for (FunctionIdentifier fi = FIRST_FUNCTION; fi < N_FUNCTION_IDS; ++fi) + { + if (len == strlen(tfunc[fi].name) && strncmp(s, tfunc[fi].name, len) == 0) + return fi; + } + return NOT_A_FUNCTION; +} +/*}}}*/ + + /* sci_func -- map a double to a double */ /*{{{*/ -static Token sci_func(int argc, const Token argv[], double (*func)(double), const char *func_name) +static Token sci_func(int argc, const Token argv[], FltT (*func)(FltT), const char *func_name) { Token result; @@ -251,9 +265,9 @@ static Token sci_func(int argc, const Token argv[], double (*func)(double), cons { if (argc==1 && argv[0].type==INT) { - result.type=FLOAT; + result.type = FLOAT; errno=0; - result.u.flt=(func)((double)argv[0].u.integer); + result.u.flt = (func)((FltT)argv[0].u.integer); if (errno) { result.type=EEK; @@ -272,34 +286,39 @@ static Token sci_func(int argc, const Token argv[], double (*func)(double), cons return result; } /*}}}*/ + /* arsinh */ /*{{{*/ -static double arsinh(double x) +static FltT arsinh(FltT x) { - return log(x+sqrt(x*x+1.0)); + return LOGFLT(x + SQRTFLT(x*x + 1.0)); } /*}}}*/ + /* arcosh */ /*{{{*/ -static double arcosh(double x) +static FltT arcosh(FltT x) { - if (x>=1.0) return log(x+sqrt(x*x-1.0)); + if (x >= 1.0) return LOGFLT(x + SQRTFLT(x*x - 1.0)); else { errno=EDOM; return 0.0; } } /*}}}*/ + /* artanh */ /*{{{*/ -static double artanh(double x) +static FltT artanh(FltT x) { - if (x>-1.0 && x<1.0) return 0.5*log((1.0+x)/(1.0-x)); + if (x > -1.0 && x < 1.0) return 0.5*LOGFLT((1.0+x)/(1.0-x)); else { errno=EDOM; return 0.0; } } /*}}}*/ + /* rad2deg */ /*{{{*/ -static double rad2deg(double x) +static FltT rad2deg(FltT x) { return (360.0/(2.0*CONST_PI))*x; } /*}}}*/ + /* deg2rad */ /*{{{*/ -static double deg2rad(double x) +static FltT deg2rad(FltT x) { return (2.0*CONST_PI/360.0)*x; } @@ -315,34 +334,40 @@ static Token adr_func(int argc, const Token argv[], LocConvention lcon) /* variables */ /*{{{*/ Token result; Dimensions dim; - size_t i; /*}}}*/ if (lcon == EXCEL) return excel_adr_func(argc, argv); /* asserts */ /*{{{*/ - assert(argv != (Token*)0); + assert(argc < 1 || argv != NULLTOKEN); /*}}}*/ + result.type = LOCATION; LOCATION_GETS(result.u.location, upd_l); - if (argc == 1 && argv[0].type == LOCATION) - if (lcon == ABSOLUTE) return argv[0]; - else - { + int offset = 0; + if (argc > 0 && argv[0].type == LOCATION) + { + if (argc == 1 && lcon == RELATIVE) + { LOCATION_ADD(result.u.location, argv[0].u.location); return result; - } - for (i = 0; i < argc && i < HYPER; ++i) + } + LOCATION_GETS(result.u.location, argv[0].u.location); + offset = 1; + } + int i = offset; + while (i < argc && i - offset < HYPER) { - if (argv[0].type == EEK) return argv[0]; + if (argv[i].type == EEK) return argv[i]; if (argv[i].type == INT) - if (lcon == ABSOLUTE) result.u.location[i] = argv[i].u.integer; - else result.u.location[i] += argv[i].u.integer; + if (lcon == ABSOLUTE) result.u.location[i-offset] = argv[i].u.integer; + else result.u.location[i-offset] += argv[i].u.integer; else if (argv[i].type != EMPTY) break; + ++i; } if (i < argc) - duperror(&result, _("Usage: &([integer x][,[integer y][,[integer z]]])")); - else result.type = LOCATION; + duperror(&result, + _("Usage: &([location l,][integer x][,[integer y][,[integer z]]])")); return result; } /*}}}*/ @@ -370,7 +395,7 @@ static Token at_func(int argc, const Token argv[], LocConvention lcon) tfree(&location); return result; } - return getvalue(upd_sheet, location.u.location); + return recompvalue(upd_sheet, location.u.location); } static Token abs_adr_func(int argc, const Token argv[]) @@ -404,18 +429,13 @@ static Token excel_adr_func(int argc, const Token argv[]) { Token result; char *usage = _("Usage: X&(THERE_LABEL, HERE_LABEL, [fix_x], [fix_y], [fix_z])"); - if (argc < 2) { - duperror(&result, usage); - return result; - } + if (argc < 2) return duperror(&result, usage); if (argv[0].type == EEK) return argv[0]; if (argv[1].type == EEK) return argv[1]; if (argv[0].type != LOCATION || argv[1].type != LOCATION) - { - duperror(&result, usage); - return result; - } + return duperror(&result, usage); + result.type = LOCATION; LOCATION_GETS(result.u.location, upd_l); for (Dimensions dim = X; dim < HYPER; ++dim) @@ -425,11 +445,7 @@ static Token excel_adr_func(int argc, const Token argv[]) if (i < argc) { if (argv[i].type == EEK) return argv[i]; - if (!INTPATIBLE(argv[i])) - { - duperror(&result, usage); - return result; - } + if (!INTPATIBLE(argv[i])) return duperror(&result, usage); if (argv[i].type == INT && argv[i].u.integer > 0) fixed = true; } if (fixed) result.u.location[dim] = argv[0].u.location[dim]; @@ -496,10 +512,7 @@ static Token bitwise_func(int argc, const Token argv[], LogicalFunction lop) else accum = 0; for (size_t i = 0; i < argc; ++i) { if (!INTPATIBLE(argv[i])) - { - duperror(&result, _("Bitwise functions operate only on integers.")); - return result; - } + return duperror(&result, _("Bitwise functions need integer arguments.")); int val = 0; if (argv[i].type == INT) val = argv[i].u.integer; if (lop == LOG_AND) accum = accum & val; @@ -527,14 +540,32 @@ static Token e_func(int argc, const Token argv[]) Token result; /*}}}*/ - if (argc==0) /* result is constant e */ /*{{{*/ + if (argc < 1) /* result is constant e */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=CONST_E; + result.type = FLOAT; + result.u.flt = CONST_E; + return result; } + + return duperror(&result, _("Usage: e()")); +} +/*}}}*/ + +/* tau = 2pi */ /*{{{*/ +static Token tau_func(int argc, const Token argv[]) +{ + /* variables */ /*{{{*/ + Token result; /*}}}*/ - else duperror(&result, _("Usage: e()")); - return result; + + if (argc < 1) /* result is constant e */ /*{{{*/ + { + result.type = FLOAT; + result.u.flt = 2.0*CONST_PI; + return result; + } + + return duperror(&result, _("Usage: tau()")); } /*}}}*/ @@ -546,17 +577,17 @@ static Token eval_func(int argc, const Token argv[]) /*}}}*/ --max_eval; - if (max_eval<0) duperror(&result, _("nested eval()")); - else if (argc==1 && argv[0].type==LOCATION) + if (max_eval < 0) duperror(&result, _("nested eval()")); + else if (argc == 1 && argv[0].type==LOCATION) /* evaluate expression in cell at given position */ /*{{{*/ { - Token **contents; + Token contents; - contents = EMPTY_TVEC; + contents.type = EMPTY; if (LOC_WITHIN(upd_sheet, argv[0].u.location)) - contents = getcont(CELL_AT(upd_sheet,argv[0].u.location), CONTINGENT); - if (contents == EMPTY_TVEC) result.type = EMPTY; - else result = eval(contents, FULL); + contents = gettok(CELL_AT(upd_sheet,argv[0].u.location), CONTINGENT); + if (contents.type == EMPTY) result.type = EMPTY; + else result = evaltoken(contents, FULL); } /*}}}*/ else duperror(&result, _("Usage: eval(location)")); @@ -586,64 +617,35 @@ static Token string_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; - Cell *cell; - char *buf; + const char *usage = _("Usage: string(x[,[int prec][,decimal|scientific]])"); + int precision = def_precision; + bool use_sci = DEF_SCIENTIFIC; char staticbuf[4096]; size_t size; /*}}}*/ - if (argc==1 && argv[0].type==LOCATION) /* cell to string */ /*{{{*/ + if (argc < 1 || argc > 3) return duperror(&result, usage); + if (argc == 3) { - result.type=STRING; - if (LOC_WITHIN(upd_sheet, argv[0].u.location)) - { - cell = CELL_AT(upd_sheet, argv[0].u.location); - printvalue(staticbuf, sizeof(staticbuf), 0, 0, getscientific(cell), - getprecision(cell), upd_sheet, argv[0].u.location); - result.u.string = malloc(strlen(staticbuf) + 1); - strcpy(result.u.string, staticbuf); - } - else - { - result.u.string = malloc(1); - result.u.string[0] = '\0'; - } + if (argv[2].type != FIDENT) return duperror(&result, usage); + switch (argv[2].u.fident) + { + case FUNC_DECIMAL: use_sci = false; break; + case FUNC_SCIENTIFIC: use_sci = true; break; + default: assert(0); + } } - /*}}}*/ - else if (argc==1 && argv[0].type==FLOAT) /* float to string */ /*{{{*/ + if (argc >= 2 & argv[1].type != EMPTY) { - result.u.string=malloc(def_precision+10); - sprintf(result.u.string,DEF_SCIENTIFIC ? "%.*e" : "%.*f", def_precision,argv[0].u.flt); - result.type=STRING; + if (argv[1].type != INT) return duperror(&result, usage); + precision = argv[1].u.integer; } - /*}}}*/ - else if (argc==1 && argv[0].type==INT) /* int to string */ /*{{{*/ - { - int length=2; - int n=argv[0].u.integer; - - while (n!=0) n/=10; - result.u.string=malloc(length); - sprintf(result.u.string,"%ld",argv[0].u.integer); - result.type=STRING; - } - /*}}}*/ - else if (argc==2 && argv[0].type==FLOAT && argv[1].type==INT) /* float to string */ /*{{{*/ - { - result.u.string=malloc(argv[1].u.integer==-1 ? def_precision+10 : argv[1].u.integer+10); - sprintf(result.u.string,DEF_SCIENTIFIC ? "%.*e" : "%.*f",argv[1].u.integer==-1 ? def_precision : (int)(argv[1].u.integer), argv[0].u.flt); - result.type=STRING; - } - /*}}}*/ - else if (argc==3 && argv[0].type==FLOAT && (argv[1].type==INT || argv[1].type==EMPTY) && argv[2].type==INT) /* float to string */ /*{{{*/ - { - result.u.string=malloc((argv[1].type==INT ? argv[1].u.integer : def_precision)+10); - sprintf(result.u.string,argv[2].u.integer ? "%.*e" : "%.*f",argv[1].type==INT && argv[1].u.integer>=0 ? (int)argv[1].u.integer : def_precision, argv[0].u.flt); - result.type=STRING; - } - /*}}}*/ - else /* return string type error */ /*{{{*/ - duperror(&result, _("Usage: string(location)|string(int)|string(float[,[int][,integer]])")); + result.type = STRING; + size = printtok(staticbuf, sizeof(staticbuf), 0, false, use_sci, precision, + true, argv); + if (size > sizeof(staticbuf) - 2) + return duperror(&result, _("string: Overflow of internal buffer")); + result.u.string = strdup(staticbuf); return result; } /*}}}*/ @@ -689,10 +691,7 @@ static Token sum_func(int argc, const Token argv[]) Token result; const char *usage = _("Usage: sum(loc_start, loc_end)|sum(val1, val2,...)"); - if (argc <= 0) { - duperror(&result, usage); - return result; - } + if (argc <= 0) return duperror(&result, usage); if (argc == 2 && argv[0].type == LOCATION && argv[1].type == LOCATION) /* result is sum of entries in range */ /*{{{*/ { @@ -713,7 +712,7 @@ static Token sum_func(int argc, const Token argv[]) { Token t; - tmp = tadd(result, t=getvalue(upd_sheet,w)); + tmp = tadd(result, t=recompvalue(upd_sheet,w)); tfree(&t); tfree(&result); result=tmp; @@ -730,36 +729,60 @@ static Token n_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; + const char *usage = _("Usage: n([location[,location]])|n(val1,val2,...)"); + Location w; + bool singleloc = true; + bool loclist = true; /*}}}*/ - if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION) - /* result is number of elements */ /*{{{*/ - { - /* variables */ /*{{{*/ - Location w; - int x1,y1,z1; - int x2,y2,z2; - Token tmp; - int n; - /*}}}*/ - - x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2); - y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2); - z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2); - n=0; - for (w[X]=x1; w[X]<=x2; ++(w[X])) - for (w[Y]=y1; w[Y]<=y2; ++(w[Y])) - for (w[Z]=z1; w[Z]<=z2; ++(w[Z])) - { - tmp = getvalue(upd_sheet, w); - if (tmp.type != EMPTY) ++n; - tfree(&tmp); - } - result.type = INT; - result.u.integer = n; + if (argc < 0) return duperror(&result, usage); + result.type = INT; + result.u.integer = 0; + switch (argc) { + case 0: LOCATION_GETS(w, upd_l); break; + case 1: + if (argv[0].type == LOCATION) LOCATION_GETS(w, argv[0].u.location); + else singleloc = false; + break; + case 2: + if (argv[0].type == LOCATION && argv[1].type == LOCATION) + loclist = false; + /* FALL THROUGH */ + case 3: + singleloc = false; } - /*}}}*/ - else duperror(&result, _("Usage: n(location,location)")); + + if (singleloc) { + Token tmp = recompvalue(upd_sheet, w); + result.u.integer = (tmp.type != EMPTY); + tfree(&tmp); + return result; + } + + if (loclist) { + for (int i = 0; i < argc; ++i) + switch (argv[i].type) { + case EEK: return argv[i]; + case EMPTY: break; + default: result.u.integer++; + } + return result; + } + + int x1, y1, z1; + int x2, y2, z2; + x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2); + y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2); + z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2); + + for (w[X]=x1; w[X]<=x2; ++(w[X])) + for (w[Y]=y1; w[Y]<=y2; ++(w[Y])) + for (w[Z]=z1; w[Z]<=z2; ++(w[Z])) + { + Token tmp = recompvalue(upd_sheet, w); + if (tmp.type != EMPTY) result.u.integer++; + tfree(&tmp); + } return result; } /*}}}*/ @@ -806,8 +829,8 @@ static Token binop_func(int argc, const Token argv[], { if (argc == 2) return tfunc(argv[0], argv[1]); Token err; - duperror(&err, _("Binary infix op as function requires exactly 2 args")); - return err; + return + duperror(&err, _("Binary infix op as function requires exactly 2 args")); } /*}}}*/ @@ -868,51 +891,45 @@ static Token int_func(int argc, const Token argv[]) Token result; /*}}}*/ - if (argc==1 && argv[0].type==FLOAT) + if (argc == 1 && argv[0].type == FLOAT) /* result is integer with cutoff fractional part */ /*{{{*/ { - result.type=INT; - result.u.integer=(long)(argv[0].u.flt); + result.type = INT; + result.u.integer = (IntT)argv[0].u.flt; + return result; } /*}}}*/ - else if (argc==3 && argv[0].type==FLOAT && argv[1].type==INT && argv[2].type==INT) + if (argc == 2 && argv[0].type==FLOAT && argv[1].type == FIDENT) /* result is integer with given conversion */ /*{{{*/ { - result.type=INT; - if (argv[0].u.flt<0) + result.type = INT; + switch (argv[1].u.fident) { - if (argv[1].u.integer<-1) result.u.integer=(long)floor(argv[0].u.flt); - else if (argv[1].u.integer==-1) result.u.integer=(long)(argv[0].u.flt-0.5); - else if (argv[1].u.integer==0) result.u.integer=(long)(argv[0].u.flt); - else if (argv[1].u.integer==1) result.u.integer=(long)(argv[0].u.flt+0.5); - else result.u.integer=(long)ceil(argv[0].u.flt); - } - else - { - if (argv[2].u.integer<-1) result.u.integer=(long)floor(argv[0].u.flt); - else if (argv[2].u.integer==-1) result.u.integer=(long)(argv[0].u.flt-0.5); - else if (argv[2].u.integer==0) result.u.integer=(long)(argv[0].u.flt); - else if (argv[2].u.integer==1) result.u.integer=(long)(argv[0].u.flt+0.5); - else result.u.integer=(long)ceil(argv[0].u.flt); + case FUNC_TRUNC: result.u.integer = (IntT)argv[0].u.flt; break; + case FUNC_ROUND: result.u.integer = ROUNDFLTINT(argv[0].u.flt); break; + case FUNC_FLOOR: result.u.integer = (IntT)FLOORFLT(argv[0].u.flt); break; + case FUNC_CEIL: result.u.integer = (IntT) CEILFLT(argv[0].u.flt); break; + default: assert(0); } + return result; } /*}}}*/ - else if (argc==1 && argv[0].type==STRING) + if (argc == 1 && argv[0].type == STRING) /* result is integer */ /*{{{*/ { char *s; errno=0; - result.u.integer=strtol(argv[0].u.string,&s,10); + result.u.integer = STRTOINT(argv[0].u.string, &s, 10); if (s==(char*)0 || *s) duperror(&result, _("int(string): invalid string")); else if (errno==ERANGE && (result.u.integer==LONG_MAX || result.u.integer==LONG_MIN)) duperror(&result, _("int(string): domain error")); - else result.type=INT; + else result.type = INT; + return result; } /*}}}*/ - else duperror(&result, _("Usage: int(float[,integer,integer])")); - return result; + return duperror(&result, _("Usage: int(string|float[,rounding_function])")); } /*}}}*/ @@ -921,14 +938,14 @@ static Token frac_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; - double foo; + FltT foo; /*}}}*/ if (argc==1 && argv[0].type==FLOAT) /* result is fractional part */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=modf(argv[0].u.flt,&foo); + result.type = FLOAT; + result.u.flt = MODFFLT(argv[0].u.flt, &foo); } /*}}}*/ else duperror(&result, _("Usage: frac(float)")); @@ -958,34 +975,35 @@ static Token len_func(int argc, const Token argv[]) static Token log_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ - double x=-1.0,y=-1.0; + FltT x=-1.0, y=-1.0; Token result; /*}}}*/ /* set x and y to first two arguments */ /*{{{*/ if (argc>=1) { - if (argv[0].type==FLOAT) x=argv[0].u.flt; - else if (argv[0].type==INT) x=(double)argv[0].u.integer; + if (argv[0].type == FLOAT) x = argv[0].u.flt; + else if (argv[0].type == INT) x = (FltT)argv[0].u.integer; } if (argc==2) { - if (argv[1].type==FLOAT) y=argv[1].u.flt; - else if (argv[1].type==INT) y=(double)argv[1].u.integer; + if (argv[1].type == FLOAT) y = argv[1].u.flt; + else if (argv[1].type == INT) y = (FltT)argv[1].u.integer; } /*}}}*/ if (argc==1 && (argv[0].type==FLOAT || argv[0].type==INT)) /* result is ln(x) */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=log(x); + result.type = FLOAT; + result.u.flt = LOGFLT(x); } /*}}}*/ else if (argc==2 && (argv[0].type==FLOAT || argv[0].type==INT) && (argv[1].type==FLOAT || argv[1].type==INT)) /* result is ln(x)/ln(y) */ /*{{{*/ { - result.type=FLOAT; - if (y==CONST_E) result.u.flt=log(x); - else if (y==10.0) result.u.flt=log10(x); - else result.u.flt=log(x)/log(y); + result.type = FLOAT; + if (y == CONST_E) result.u.flt = LOGFLT(x); + else if (y == 2.0) result.u.flt = LOG2FLT(x); + else if (y == 10.0) result.u.flt = LOG10FLT(x); + else result.u.flt = LOGFLT(x)/LOGFLT(y); } /*}}}*/ else duperror(&result, _("Usage: log(float[,float])")); @@ -1015,7 +1033,7 @@ static Token minmax_func(int argc, const Token argv[], int min) z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2); minloc[X] = x1; minloc[Y] = y1; minloc[Z] = z1; - result = getvalue(upd_sheet, minloc); + result = recompvalue(upd_sheet, minloc); for (w[X]=x1; w[X]<=x2; ++(w[X])) for (w[Y]=y1; w[Y]<=y2; ++(w[Y])) @@ -1023,19 +1041,19 @@ static Token minmax_func(int argc, const Token argv[], int min) { Token t; - t = getvalue(upd_sheet, w); + t = recompvalue(upd_sheet, w); tmp = (min ? tle(result,t) : tge(result, t)); - if (tmp.type==INT) + if (tmp.type == INT) /* successful comparison */ /*{{{*/ { - tfree(&tmp); if (tmp.u.integer == 0) { tfree(&result); - result=t; + result = t; LOCATION_GETS(minloc, w); } else tfree(&t); + tfree(&tmp); } /*}}}*/ else @@ -1099,8 +1117,8 @@ static Token abs_func(int argc, const Token argv[]) if (argc==1 && argv[0].type==FLOAT) /* result is absolute floating point number */ /*{{{*/ { - result.type=FLOAT; - result.u.flt=fabs(argv[0].u.flt); + result.type = FLOAT; + result.u.flt = ABSFLT(argv[0].u.flt); } /*}}}*/ else if (argc==1 && argv[0].type==INT) @@ -1138,20 +1156,27 @@ static Token env_func(int argc, const Token argv[]) static Token float_func(int argc, const Token argv[]) { Token result; - - if (argc==1 && argv[0].type==STRING) - /* convert string to float */ /*{{{*/ - { - char *p; + const char *usage = _("Usage: float(string|int)"); - result.u.flt = strtod(argv[0].u.string, &p); - if (p != argv[0].u.string && *p=='\0' + if (argc != 1) return duperror(&result, usage); + switch (argv[0].type) + { + case STRING: { + char *p; + + result.u.flt = STRTOFLT(argv[0].u.string, &p); + if (p != argv[0].u.string && *p=='\0' && dblfinite(result.u.flt) == (const char*)0) - result.type = FLOAT; - else duperror(&result, _("Not a (finite) floating point number")); + result.type = FLOAT; + else duperror(&result, _("Not a (finite) floating point number")); + return result; + } + case INT: + result.type = FLOAT; + result.u.flt = (FltT)argv[0].u.integer; + return result; } - /*}}}*/ - else duperror(&result, _("Usage: float(string)")); + return duperror(&result, usage); return result; } /*}}}*/ @@ -1243,11 +1268,10 @@ static Token poly_func(int argc, const Token argv[]) const char *usage = _("Usage: poly(float|integer,float|integer,...)"); result.type = EMPTY; - if (argc < 2) duperror(&result, usage); - else for (size_t i = 0; i < argc; ++i) - if (argv[i].type != INT && argv[i].type != FLOAT) - duperror(&result, usage); - if (result.type == EEK) return result; + if (argc < 2) return duperror(&result, usage); + for (size_t i = 0; i < argc; ++i) + if (argv[i].type != INT && argv[i].type != FLOAT) + return duperror(&result, usage); result = tcopy(argv[1]); for (size_t i = 2; i < argc; ++i) @@ -1264,101 +1288,177 @@ static Token poly_func(int argc, const Token argv[]) } /*}}}*/ +/* sqrt */ /*{{{*/ +static Token sqrt_func(int argc, const Token argv[]) +{ + return sci_func(argc, argv, SQRTFLT, "sqrt"); +} + /* sin */ /*{{{*/ static Token sin_func(int argc, const Token argv[]) { - return sci_func(argc,argv,sin,"sin"); + return sci_func(argc, argv, SINFLT, "sin"); } /*}}}*/ /* cos */ /*{{{*/ static Token cos_func(int argc, const Token argv[]) { - return sci_func(argc,argv,cos,"cos"); + return sci_func(argc, argv, COSFLT, "cos"); } /*}}}*/ /* tan */ /*{{{*/ static Token tan_func(int argc, const Token argv[]) { - return sci_func(argc,argv,tan,"tan"); + return sci_func(argc, argv, TANFLT, "tan"); } /*}}}*/ /* sinh */ /*{{{*/ static Token sinh_func(int argc, const Token argv[]) { - return sci_func(argc,argv,sinh,"sinh"); + return sci_func(argc, argv, SINHFLT, "sinh"); } /*}}}*/ /* cosh */ /*{{{*/ static Token cosh_func(int argc, const Token argv[]) { - return sci_func(argc,argv,cosh,"cosh"); + return sci_func(argc, argv, COSHFLT, "cosh"); } /*}}}*/ /* tanh */ /*{{{*/ static Token tanh_func(int argc, const Token argv[]) { - return sci_func(argc,argv,tanh,"tanh"); + return sci_func(argc, argv, TANHFLT, "tanh"); } /*}}}*/ /* asin */ /*{{{*/ static Token asin_func(int argc, const Token argv[]) { - return sci_func(argc,argv,asin,"asin"); + return sci_func(argc, argv, ASINFLT, "asin"); } /*}}}*/ /* acos */ /*{{{*/ static Token acos_func(int argc, const Token argv[]) { - return sci_func(argc,argv,acos,"acos"); + return sci_func(argc, argv, ACOSFLT, "acos"); } /*}}}*/ /* atan */ /*{{{*/ static Token atan_func(int argc, const Token argv[]) { - return sci_func(argc,argv,atan,"atan"); + return sci_func(argc, argv, ATANFLT, "atan"); } /*}}}*/ /* arsinh */ /*{{{*/ static Token arsinh_func(int argc, const Token argv[]) { - return sci_func(argc,argv,arsinh,"arsinh"); + return sci_func(argc, argv, arsinh, "arsinh"); } /*}}}*/ /* arcosh */ /*{{{*/ static Token arcosh_func(int argc, const Token argv[]) { - return sci_func(argc,argv,arcosh,"arcosh"); + return sci_func(argc, argv, arcosh, "arcosh"); } /*}}}*/ /* artanh */ /*{{{*/ static Token artanh_func(int argc, const Token argv[]) { - return sci_func(argc,argv,artanh,"artanh"); + return sci_func(argc, argv, artanh, "artanh"); } /*}}}*/ /* rad2deg */ /*{{{*/ static Token rad2deg_func(int argc, const Token argv[]) { - return sci_func(argc,argv,rad2deg,"rad2deg"); + return sci_func(argc, argv, rad2deg, tfunc[FUNC_RAD2DEG].name); } /*}}}*/ /* deg2rad */ /*{{{*/ static Token deg2rad_func(int argc, const Token argv[]) { - return sci_func(argc,argv,deg2rad,"deg2rad"); + return sci_func(argc, argv, deg2rad, tfunc[FUNC_DEG2RAD].name); +} +/*}}}*/ + +/* self function, typically used for keywords */ /*{{{*/ +static Token self_func(int argc, const Token argv[], FunctionIdentifier which) +{ + Token result; + if (argc == -1) + { + result.type = FIDENT; + result.u.fident = which; + return result; + } + const char *templ = _("%s may only be used as a bare identifier"); + result.type = EEK; + result.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 1); + sprintf(result.u.err, templ, tfunc[which].name); + return result; +} +/*}}}*/ + +/* decimal keyword */ /*{{{*/ +static Token decimal_func(int argc, const Token argv[]) +{ + return self_func(argc, argv, FUNC_DECIMAL); +} +/*}}}*/ + +/* scientific keyword */ /*{{{*/ +static Token scientific_func(int argc, const Token argv[]) +{ + return self_func(argc, argv, FUNC_SCIENTIFIC); +} +/*}}}*/ + +/* rounding function */ /*{{{*/ +static Token rounding_func(int argc, const Token argv[], + FunctionIdentifier which_func, + FltT (*func)(FltT)) +{ + if (argc == -1) return self_func(argc, argv, which_func); + return sci_func(argc, argv, func, tfunc[which_func].name); +} +/*}}}*/ + +/* floor */ /*{{{*/ +static Token floor_func(int argc, const Token argv[]) +{ + return rounding_func(argc, argv, FUNC_FLOOR, FLOORFLT); +} +/*}}}*/ + +/* ceil */ /*{{{*/ +static Token ceil_func(int argc, const Token argv[]) +{ + return rounding_func(argc, argv, FUNC_CEIL, CEILFLT); +} +/*}}}*/ + +/* round */ /*{{{*/ +static Token round_func(int argc, const Token argv[]) +{ + return rounding_func(argc, argv, FUNC_ROUND, ROUNDFLT); +} +/*}}}*/ + +/* round */ /*{{{*/ +static Token trunc_func(int argc, const Token argv[]) +{ + return rounding_func(argc, argv, FUNC_TRUNC, TRUNCFLT); } /*}}}*/ @@ -1371,8 +1471,8 @@ static Token rnd_func(int argc, const Token argv[]) if (argc==0) { - result.type=FLOAT; - result.u.flt=rand()/((double)RAND_MAX); + result.type = FLOAT; + result.u.flt = rand()/((FltT)RAND_MAX); } else duperror(&result, _("Usage: rnd()")); return result; @@ -1384,22 +1484,25 @@ static Token substr_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; + const char *usage = _("Usage: substr(string,integer[,integer])"); /*}}}*/ - if (argc==3 && argv[0].type==STRING && argv[1].type==INT && argv[2].type==INT) + if (argc < 2 || argc > 3) return duperror(&result, usage); + if (argc == 3 && argv[2].type != INT) duperror(&result, usage); + if (argv[0].type == STRING && argv[1].type == INT) { char ss[1024]; - int n, l, b, e; - b = argv[1].u.integer; - e = argv[2].u.integer; - l = strlen(argv[0].u.string); - if( b < 0 ) b = 0; - if( b > l ) b = l; - if( e > l ) e = l; - n = e - b + 1; - if( n >= 1024 ) n = 1024 - 1; - if(n > 0) { + int b = argv[1].u.integer; + int l = strlen(argv[0].u.string); + int e = l; + if (argc == 3) e = argv[2].u.integer; + if ( b < 0 ) b = 0; + if ( b > l ) b = l; + if ( e > l ) e = l; + int n = e - b + 1; + if ( n >= 1024 ) n = 1024 - 1; + if (n > 0) { ss[n] = '\0'; strncpy(ss, argv[0].u.string + b, n); result.type=STRING; @@ -1409,7 +1512,7 @@ static Token substr_func(int argc, const Token argv[]) result.type=EMPTY; } } - else duperror(&result, _("Usage: substr(string,integer,integer)")); + else duperror(&result, usage); return result; } /*}}}*/ @@ -1443,12 +1546,12 @@ static Token time_func(int argc, const Token argv[]) { Token result; - if (argc==0) + if (argc < 1) { - result.type=INT; - result.u.integer=time((time_t*)0); + result.type = INT; + result.u.integer = time((time_t*)0); } - else duperror(&result, _("Usage: time()")); + else duperror(&result, _("Usage: time[()]")); return result; } /*}}}*/ @@ -1459,8 +1562,7 @@ static Token negate_func(int argc, const Token argv[]) if (argc != 1) { Token err; - duperror(&err, _("Usage: -EXPR|negate(expr)")); - return err; + return duperror(&err, _("Usage: -EXPR|negate(expr)")); } return tneg(argv[0]); } @@ -1470,68 +1572,94 @@ static Token negate_func(int argc, const Token argv[]) compatible, new entries should be appended. */ Tfunc tfunc[]= { - { "@", abs_at_func }, - { "&", abs_adr_func }, - { "x", x_func }, - { "y", y_func }, - { "z", z_func }, - { "eval", eval_func }, - { "error", error_func }, - { "string", string_func }, - { "sum", sum_func }, - { "n", n_func }, - { "int", int_func }, - { "frac", frac_func }, - { "len", len_func }, - { "min", min_func }, - { "max", max_func }, - { "abs", abs_func }, - { "$", env_func }, - { "float", float_func }, - { "strftime", strftime_func }, - { "clock", clock_func }, - { "poly", poly_func }, - { "e", e_func }, - { "log", log_func }, - { "sin", sin_func }, - { "cos", cos_func }, - { "tan", tan_func }, - { "sinh", sinh_func }, - { "cosh", cosh_func }, - { "tanh", tanh_func }, - { "asin", asin_func }, - { "acos", acos_func }, - { "atan", atan_func }, - { "arsinh", arsinh_func }, - { "arcosh", arcosh_func }, - { "artanh", artanh_func }, - { "deg2rad", deg2rad_func }, - { "rad2deg", rad2deg_func }, - { "rnd", rnd_func }, - { "substr", substr_func }, - { "strptime", strptime_func }, - { "time", time_func }, - { "bitand", bitand_func }, - { "bitor", bitor_func }, - { "R", rel_at_func }, - { "D", rel_adr_func }, - { "X", excel_at_func }, - { "X&", excel_adr_func }, - { "negate", negate_func }, - { "+", plus_func }, - { "-", minus_func }, - { "*", mul_func }, - { "/", div_func }, - { "<=", le_func }, - { ">=", ge_func }, - { "<", lt_func }, - { ">", gt_func }, - { "==", isequal_func }, - { "~=", abouteq_func }, - { "!=", ne_func }, - { "^", pow_func }, - { "%", mod_func }, - { "concat", concat_func }, - { "", (Token (*)(int, const Token[]))0 } + /* Operators in order of increasing precedence */ + [FUNC_CONCAT] = { "concat", concat_func, INFIX_CONC, FUNCT, "" }, + [FUNC_LESS_EQUAL] = { "<=", le_func, INFIX_REL, FUNCT, 0 }, + [FUNC_GREATER_EQUAL] = { ">=", ge_func, INFIX_REL, FUNCT, 0 }, + [FUNC_LESS_THAN] = { "<", lt_func, INFIX_REL, FUNCT, 0 }, + [FUNC_GREATER_THAN] = { ">", gt_func, INFIX_REL, FUNCT, 0 }, + [FUNC_EQUAL_EQUAL] = { "==", isequal_func, INFIX_REL, FUNCT, 0 }, + [FUNC_TILDE_EQUAL] = { "~=", abouteq_func, INFIX_REL, FUNCT, 0 }, + [FUNC_BANG_EQUAL] = { "!=", ne_func, INFIX_REL, FUNCT, 0 }, + [FUNC_PLUS_SYMBOL] = { "+", plus_func, INFIX_PLUS, FUNCT, 0 }, + [FUNC_MINUS_SYMBOL] = { "-", minus_func, INFIX_PLUS, FUNCT, 0 }, + [FUNC_ASTERISK] = { "*", mul_func, INFIX_MUL, FUNCT, 0 }, + [FUNC_SLASH] = { "/", div_func, INFIX_MUL, FUNCT, 0 }, + [FUNC_PER_CENT] = { "%", mod_func, INFIX_MUL, FUNCT, 0 }, + [FUNC_NEGATE] = { "negate", negate_func, PREFIX_NEG, FUNCT, "-" }, + [FUNC_CARET] = { "^", pow_func, INFIX_POW, FUNCT, 0 }, + + /* Addressing/cell fetching/cell-position functions */ + [FUNC_AMPERSAND] = { "&", abs_adr_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_D] = { "D", rel_adr_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_X_AMPERSAND] = { "X&", excel_adr_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_AT_SYMBOL] = { "@", abs_at_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_R] = { "R", rel_at_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_CAP_X] = { "X", excel_at_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_X] = { "x", x_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_Y] = { "y", y_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_Z] = { "z", z_func, PREFIX_FUNC, FUNCT, 0 }, + + /* Evaluation control functions */ + [FUNC_CLOCK] = { "clock", clock_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ERROR] = { "error", error_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_EVAL] = { "eval", eval_func, PREFIX_FUNC, FUNCT, 0 }, + + /* Type conversion functions */ + [FUNC_FLOAT] = { "float", float_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_INT] = { "int", int_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_STRING] = { "string", string_func, PREFIX_FUNC, FUNCT, 0 }, + + /* Block operations */ + [FUNC_MAX] = { "max", max_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_MIN] = { "min", min_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_N] = { "n", n_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_SUM] = { "sum", sum_func, PREFIX_FUNC, FUNCT, 0 }, + + /* String functions */ + [FUNC_LEN] = { "len", len_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_SUBSTR] = { "substr", substr_func, PREFIX_FUNC, FUNCT, 0 }, + + /* Transcendental functions */ + [FUNC_ACOS] = { "acos", acos_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ARCOSH] = { "arcosh", arcosh_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ARSINH] = { "arsinh", arsinh_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ARTANH] = { "artanh", artanh_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ASIN] = { "asin", asin_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ATAN] = { "atan", atan_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_DEG2RAD] = { "deg2rad", deg2rad_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_E] = { "e", e_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_COS] = { "cos", cos_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_COSH] = { "cosh", cosh_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_LOG] = { "log", log_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_RAD2DEG] = { "rad2deg", rad2deg_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_SIN] = { "sin", sin_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_SINH] = { "sinh", sinh_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_TAN] = { "tan", tan_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_TANH] = { "tanh", tanh_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_TAU] = { "tau", tau_func, PREFIX_FUNC, FUNCT, 0 }, + + /* Other mathematical functions */ + [FUNC_ABS] = { "abs", abs_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_BITAND] = { "bitand", bitand_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_BITOR] = { "bitor", bitor_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_CEIL] = { "ceil", ceil_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_FLOOR] = { "floor", floor_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_FRAC] = { "frac", frac_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_POLY] = { "poly", poly_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_RND] = { "rnd", rnd_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ROUND] = { "round", round_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_SQRT] = { "sqrt", sqrt_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_TRUNC] = { "trunc", trunc_func, PREFIX_FUNC, FUNCT, 0 }, + + /* Time functions */ + [FUNC_STRFTIME] = { "strftime", strftime_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_STRPTIME] = { "strptime", strptime_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_TIME] = { "time", time_func, PREFIX_FUNC, FUNCT, 0 }, + + /* Miscellaneous other functions/keywords */ + [FUNC_DOLLAR_SIGN] = { "$", env_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_DECIMAL] = { "decimal", decimal_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_SCIENTIFIC] = { "scientific", scientific_func, PREFIX_FUNC, FUNCT, 0 }, }; /*}}}*/ diff --git a/src/common/func.h b/src/common/func.h index 14c0277..95ff17f 100644 --- a/src/common/func.h +++ b/src/common/func.h @@ -1,14 +1,56 @@ #ifndef FUNC_H #define FUNC_H -#include "scanner.h" +#define MAX_FUNC_NAME_LENGTH 20 + +typedef enum + /* The purpose of this enum is to enforce backward compatibility + of numeric function identifiers which may be recorded in old + save files. Hence, do NOT change the order of the items in this + enum, merely add new identifiers at the end, just before + N_FUNCTION_IDENTIFIERS. + */ +{ + NOT_A_FUNCTION = -1, + FIRST_FUNCTION = 0, + FUNC_AT_SYMBOL = 0, FUNC_AMPERSAND, FUNC_X, FUNC_Y, FUNC_Z, FUNC_EVAL, + FUNC_ERROR, FUNC_STRING, FUNC_SUM, FUNC_N, FUNC_INT, FUNC_FRAC, FUNC_LEN, + FUNC_MIN, FUNC_MAX, FUNC_ABS, FUNC_DOLLAR_SIGN, FUNC_FLOAT, FUNC_STRFTIME, + FUNC_CLOCK, FUNC_POLY, FUNC_E, FUNC_LOG, FUNC_SIN, FUNC_COS, FUNC_TAN, + FUNC_SINH, FUNC_COSH, FUNC_TANH, FUNC_ASIN, FUNC_ACOS, FUNC_ATAN, + FUNC_ARSINH, FUNC_ARCOSH, FUNC_ARTANH, FUNC_DEG2RAD, FUNC_RAD2DEG, + FUNC_RND, FUNC_SUBSTR, FUNC_STRPTIME, FUNC_TIME, FUNC_BITAND, FUNC_BITOR, + FUNC_R, FUNC_D, FUNC_CAP_X, FUNC_X_AMPERSAND, FUNC_NEGATE, FUNC_PLUS_SYMBOL, + FUNC_MINUS_SYMBOL, FUNC_ASTERISK, FUNC_SLASH, + FUNC_LESS_EQUAL, FUNC_GREATER_EQUAL, FUNC_LESS_THAN, FUNC_GREATER_THAN, + FUNC_EQUAL_EQUAL, FUNC_TILDE_EQUAL, FUNC_BANG_EQUAL, FUNC_CARET, + FUNC_PER_CENT, FUNC_CONCAT, FUNC_TAU, FUNC_SQRT, FUNC_FLOOR, FUNC_CEIL, + FUNC_TRUNC, FUNC_ROUND, FUNC_DECIMAL, FUNC_SCIENTIFIC, + + N_FUNCTION_IDS +} FunctionIdentifier; + +/* Forward declaration of Token, since this header is used in scanner.h */ +typedef struct Token_struc Token; + +typedef enum /* In increasing order of precedence */ + { + INFIX_CONC, INFIX_REL, INFIX_PLUS, INFIX_MUL, PREFIX_NEG, + INFIX_POW, PREFIX_FUNC + } FunctionPrecedence; + +typedef enum { FUNCT, MACRO } EvaluationStrategy; typedef struct { - const char name[20]; - Token (*func)(int, const Token[]); + const char name[MAX_FUNC_NAME_LENGTH + 1]; + Token (*func)(int, const Token*); + FunctionPrecedence precedence; + EvaluationStrategy eval_as; + const char* display_symbol; } Tfunc; +FunctionIdentifier identcode(const char *s, size_t len); extern Tfunc tfunc[]; #endif diff --git a/src/common/main.c b/src/common/main.c index 28c9dbf..2e8aeb6 100644 --- a/src/common/main.c +++ b/src/common/main.c @@ -43,6 +43,7 @@ extern char *strdup(const char* s); #include "parser.h" #include "sheet.h" #include "wk1.h" +#include "xdr.h" /*}}}*/ /* variables */ /*{{{*/ @@ -157,7 +158,7 @@ int doanyway(Sheet *sheet, const char *msg) /*}}}*/ /* do_edit -- set or modify cell contents */ /*{{{*/ -static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv) +static int do_edit(Sheet *cursheet, Key c, const char *expr, TokVariety tv) { /* variables */ /*{{{*/ char buf[1024]; @@ -165,6 +166,7 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv) size_t x,offx; Location scur; Token **t; + Token newcont; Cell *cell; /*}}}*/ @@ -172,37 +174,48 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv) if (locked(cell)) line_msg(_("Edit cell:"),_("Cell is locked")); else { + newcont.type = EMPTY; LOCATION_GETS(scur, cursheet->cur); if (expr) { - s=expr; - t=scan(&s); + s = expr; + t = scan(&s); prompt = _("Cell contents:"); - if (cv == ITERATIVE) prompt = _("Clocked cell contents"); - if (*s!='\0' && t==(Token**)0) line_msg(prompt, "XXX invalid expression"); + if (tv == ITER_CONT) prompt = _("Clocked cell contents"); + if (*s != '\0') + if (t == EMPTY_TVEC) + line_msg(prompt, "XXX invalid expression"); + else + { + newcont = eval_safe(t, LITERAL); + if (newcont.type = EEK) + line_msg(prompt, "XXX unparseable expression"); + } + tvecfree(t); } else { + Token cntt; offx=0; if (c==K_NONE) { - print(buf, sizeof(buf), 0, 1, getscientific(cell), -1, - getcont(cell, cv)); - s=buf+strlen(buf); + cntt = gettok(cell, tv); + printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt); + s = buf+strlen(buf); } else if (c==K_BACKSPACE) { - print(buf, sizeof(buf), 0, 1, getscientific(cell), -1, - getcont(cell, cv)); + cntt = gettok(cell, tv); + printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt); if (strlen(buf)) *mbspos(buf+strlen(buf),-1)='\0'; - s=buf+strlen(buf); + s = buf+strlen(buf); } else if (c==K_DC) { - print(buf, sizeof(buf), 0, 1, getscientific(cell), -1, - getcont(cell, cv)); + cntt = gettok(cell, tv); + printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt); memmove(buf,mbspos(buf,1),strlen(mbspos(buf,1))+1); - s=buf; + s = buf; } else if (isalpha(c)) { @@ -222,22 +235,30 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv) { int r; - x=mbslen(buf)-mbslen(s); + tfree(&newcont); + newcont.type = EEK; + newcont.u.err = (char *)0; + x = mbslen(buf)-mbslen(s); prompt = _("Cell contents:"); - if (cv == ITERATIVE) prompt = _("Clocked cell contents:"); + if (tv == ITER_CONT) prompt = _("Clocked cell contents:"); if ((r = line_edit(cursheet, buf, sizeof(buf), prompt, &x, &offx)) < 0) return r; s = buf; - if (buf[0] == '"' && buf[strlen(buf)-1] != '"' && strlen(buf)+1 < sizeof(buf)) { - buf[strlen(buf)+1] = 0; - buf[strlen(buf)] = '"'; + if (buf[0] == '"' && buf[strlen(buf)-1] != '"' + && strlen(buf)+1 < sizeof(buf)) + { + buf[strlen(buf)+1] = 0; + buf[strlen(buf)] = '"'; } - t=scan(&s); - } while (*s!='\0' && t==(Token**)0); + t = scan(&s); + if (t != EMPTY_TVEC) { + newcont = eval_safe(t, LITERAL); + } + tvecfree(t); + } while (*s != '\0' && newcont.type == EEK); } - if (t!=(Token**)0 && *t==(Token*)0) { free(t); t=(Token**)0; } movetoloc(cursheet, scur); - putcont(cursheet, cursheet->cur, t, cv); + puttok(cursheet, cursheet->cur, newcont, tv); forceupdate(cursheet); } return 0; @@ -285,9 +306,11 @@ static int do_label(Sheet *sheet) } } while (!ok); setlabel(sheet, sheet->cur, buf,1); - if (buf[0]!='\0') + if (buf[0] != '\0' && oldlabel[0] != '\0') for (ALL_LOCS_IN_REGION(sheet,w)) relabel(sheet, w, oldlabel, buf); + cachelabels(sheet); + forceupdate(sheet); } return -1; } @@ -1217,7 +1240,8 @@ void fillwith(Sheet *she) Location dest; bool scan_labels = (src != NULLCELL && getlabel(src) != (const char*)0); /* the following is safe since we have handled copying a cell to itself */ - for (ALL_LOCS_IN_REGION(she, dest)) copycelltosheet(src, she, dest); + for (ALL_LOCS_IN_REGION(she, dest)) + copycelltosheet(src, she, dest, ALTER_LABEL); if (scan_labels) cachelabels(she); forceupdate(she); } @@ -1508,7 +1532,7 @@ static int do_goto(Sheet *sheet, const char *expr) LOCATION_GETS(upd_l, sheet->cur); upd_sheet = sheet; - value = eval(t, FULL); + value = eval_safe(t, FULL); tvecfree(t); if (value.type==LOCATION && IN_OCTANT(value.u.location)) movetoloc(sheet, value.u.location); @@ -1851,9 +1875,10 @@ int main(int argc, char *argv[]) char *end; n=strtol(optarg,&end,0); - if (*end || n<0 || n>DBL_DIG) + if (*end || n < 0 || n > LDBL_DIG) { - fprintf(stderr,_("teapot: precision must be between 0 and %d.\n"),DBL_DIG); + fprintf(stderr, + _("teapot: precision must be between 0 and %d.\n"), LDBL_DIG); exit(1); } def_precision=n; diff --git a/src/common/misc.c b/src/common/misc.c index ef9a3c1..3df4441 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -97,7 +97,7 @@ static void catchfpe(int n) caughtfpe=1; } -const char *dblfinite(double x) +const char *dblfinite(FltT x) { /*struct sigaction act; @@ -118,18 +118,19 @@ const char *dblfinite(double x) /* If one comparison was allowed, more won't hurt either. */ if (x<0.0) { - if (x<-DBL_MAX) return _("Not a (finite) floating point number"); /* -infinite */ + if (x < -FLTMX) return _("Not a (finite) floating point number"); /* -infinite */ else return (const char*)0; } else if (x>0.0) { - if (x>DBL_MAX) return _("Not a (finite) floating point number"); /* +infinite */ + if (x > FLTMX) return _("Not a (finite) floating point number"); /* +infinite */ else return (const char*)0; } else return _("Not a (finite) floating point number"); /* NaN */ } } /*}}}*/ + /* fputc_close -- error checking fputc which closes stream on error */ /*{{{*/ int fputc_close(char c, FILE *fp) { diff --git a/src/common/misc.h b/src/common/misc.h index f7ed7de..46535dd 100644 --- a/src/common/misc.h +++ b/src/common/misc.h @@ -11,7 +11,7 @@ extern "C" { void posorder(int *x, int *y); long int posnumber(const char *s, const char **endptr); -const char *dblfinite(double x); +const char *dblfinite(FltT x); int fputc_close(char c, FILE *fp); int fputs_close(const char *s, FILE *fp); void adjust(Adjust a, char *s, size_t n); diff --git a/src/common/parser.c b/src/common/parser.c index 5dd6679..fe9840a 100644 --- a/src/common/parser.c +++ b/src/common/parser.c @@ -20,6 +20,7 @@ extern char *strdup(const char* s); #include "eval.h" +#include "func.h" #include "main.h" #include "misc.h" #include "parser.h" @@ -34,6 +35,27 @@ extern char *strdup(const char* s); static Token term(Token *n[], int *i, EvalMethod meth); /*}}}*/ +/* full_eval_funcall -- evaluate the args of a funcall token and then + call the function on them +*/ +static Token full_eval_funcall(Token *t) +{ + assert(t->type == FUNCALL); + if (t->u.funcall.argc < 1) + return tfuncall(t->u.funcall.fident, t->u.funcall.argc, 0); + Token *eval_argv = malloc(t->u.funcall.argc*sizeof(Token)); + for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) { + eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL); + } + Token result = tfuncall(t->u.funcall.fident, t->u.funcall.argc, eval_argv); + /* To allow a function to return one of its arguments, we need + to be sure not to free that argument: */ + for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) + tfree_protected(&eval_argv[ai], result); + free(eval_argv); + return result; +} + /* primary -- parse and evaluate a primary term */ /*{{{*/ static Token primary(Token *n[], int *i, EvalMethod meth) { @@ -87,19 +109,7 @@ static Token primary(Token *n[], int *i, EvalMethod meth) duperror(&result, _(") expected")); return result; /*}}}*/ - case MINUS: /* return negated term */ /*{{{*/ - { - ++(*i); - if (meth == FULL) return tneg(primary(n, i, meth)); - Token arg = primary(n, i, meth); - if (arg.type == EEK) return arg; - result.type = FUNCALL; - result.u.funcall.fident = identcode("negate", 6); - result.u.funcall.argc = 1; - result.u.funcall.argv = malloc(sizeof(Token)); - result.u.funcall.argv[0] = arg; - return result; - } + /* Unary minus will be handled in powterm */ case CP: duperror(&result, _("Extra umatched ')'")); return result; @@ -200,18 +210,16 @@ static Token primary(Token *n[], int *i, EvalMethod meth) if (argc > 0) { result.u.funcall.argv = malloc(argc*sizeof(Token)); - for (size_t ai; ai < argc; ++ai) + for (size_t ai = 0; ai < argc; ++ai) result.u.funcall.argv[ai] = argv[ai]; - } + } else result.u.funcall.argv = NULLTOKEN; return result; } /*}}}*/ /* FUNCALL */ /*{{{*/ case FUNCALL: - if (meth == FULL) - result = tfuncall(n[*i]->u.funcall.fident, n[*i]->u.funcall.argc, - n[*i]->u.funcall.argv); + if (meth == FULL) result = full_eval_funcall(n[*i]); else result = tcopy(*n[*i]); ++(*i); return result; @@ -230,9 +238,45 @@ static Token powterm(Token *n[], int *i, EvalMethod meth) Token l; size_t npows = 0; + if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == MINUS) + { + /* A - symbol here is a pain. If it is being used as a function symbol, it + is higher precedence than exponentiation. If it is unary negation, + then it's lower precedence and we have to grab a powterm to the right, + and negate it. As far as I can tell the only way to tell is to + look ahead a term to see if there's a comma... + */ + bool unaryneg = true; + int j = *i + 1; + if (n[j] == NULLTOKEN) + return duperror(&l, _("A bare - is not a valid expression")); + if (n[j]->type == OPERATOR && n[j]->u.op == OP) + { + ++j; + Token dummy = term(n, &j, meth); + if (n[j] != NULLTOKEN && n[j]->type == OPERATOR && n[j]->u.op == COMMA) + unaryneg = false; + tfree(&dummy); + } + if (unaryneg) + { + ++(*i); + l = powterm(n, i, meth); + if (meth == FULL) return tneg(l); + if (l.type == EEK) return l; + if (TOKISNUM(l)) return tneg(l); + Token result; + result.type = FUNCALL; + result.u.funcall.fident = FUNC_NEGATE; + result.u.funcall.argc = 1; + result.u.funcall.argv = malloc(sizeof(Token)); + result.u.funcall.argv[0] = l; + return result; + } + } l = primary(n, i, meth); if (l.type == EEK) return l; - while (n[*i] != (Token*)0 && n[*i]->type == OPERATOR && n[*i]->u.op == POW) + while (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == POW) { Token r; @@ -255,7 +299,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth) { Token tmp = l; l.type = FUNCALL; - l.u.funcall.fident = identcode("^", 1); + l.u.funcall.fident = FUNC_CARET; l.u.funcall.argc = 1; l.u.funcall.argv = malloc(MAXARGC * sizeof(Token)); l.u.funcall.argv[0] = tmp; @@ -278,7 +322,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth) /* piterm -- parse and evaluate a product/division/modulo term */ /*{{{*/ static Token piterm(Token *n[], int *i, EvalMethod meth) { - int mulident = identcode("*", 1); + FunctionIdentifier mulident = FUNC_ASTERISK; Token l; Operator op = CP; bool first_funcall = true; @@ -317,6 +361,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth) { first_funcall = false; Token tmp = l; + l.type = FUNCALL; l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op])); l.u.funcall.argc = 2; if (op == MUL) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token)); @@ -344,7 +389,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth) /* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/ static Token factor(Token *n[], int *i, EvalMethod meth) { - int plusident = identcode("+", 1); + FunctionIdentifier plusident = FUNC_PLUS_SYMBOL; Token l; Operator op = CP; bool first_funcall = true; @@ -376,6 +421,7 @@ static Token factor(Token *n[], int *i, EvalMethod meth) { first_funcall = false; Token tmp = l; + l.type = FUNCALL; l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op])); l.u.funcall.argc = 2; if (op == PLUS) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token)); @@ -453,14 +499,15 @@ static Token term(Token *n[], int *i, EvalMethod meth) } /*}}}*/ -/* eval -- parse and evaluate token sequence */ /*{{{*/ +/* eval -- parse and evaluate nonempty token sequence + if the sequence might be empty, use eval_safe. */ /*{{{*/ Token eval(Token **n, EvalMethod meth) { Token l; int i = 0; bool first_funcall = true; - assert(upd_sheet != (Sheet*)0); + assert(meth == LITERAL || upd_sheet != (Sheet*)0); l = term(n, &i, meth); if (l.type == EEK) return l; @@ -485,10 +532,11 @@ Token eval(Token **n, EvalMethod meth) { first_funcall = false; Token tmp = l; - l.u.funcall.fident = identcode("concat", 6); + l.type = FUNCALL; + l.u.funcall.fident = FUNC_CONCAT; l.u.funcall.argc = 1; l.u.funcall.argv = malloc(MAXARGC*sizeof(Token)); - l.u.funcall.argv[0] = l; + l.u.funcall.argv[0] = tmp; } if (l.u.funcall.argc >= MAXARGC) { @@ -508,10 +556,49 @@ Token eval(Token **n, EvalMethod meth) Token eval_safe(Token **n, EvalMethod meth) { Token result; - if (n == EMPTY_TVEC) + if (n == EMPTY_TVEC || *n == NULLTOKEN) { result.type = EMPTY; return result; } return eval(n, meth); } + +/* evaltoken -- like eval, but evaluate a single token + NOTE: caller owns the result and must arrrange that it be + eventually tfree()ed */ /*{{{*/ +Token evaltoken(Token n, EvalMethod meth) +{ + if (meth == LITERAL) return tcopy(n); + switch (n.type) { + case EMPTY: return n; + case STRING: return tcopy(n); + case FLOAT: + case INT: + return n; + case OPERATOR: { + Token err; + err.type = EEK; + const char *templ = _("Attempt to eval bare operator token: "); + err.u.err = malloc(strlen(templ) + MAX_OP_NAME_LENGTH + 2); + strcpy(err.u.err, templ); + strcat(err.u.err, Op_Name[n.u.op]); + return err; + } + case LIDENT: return findlabel(upd_sheet, n.u.lident); + case FIDENT: { + Token err; + err.type = EEK; + const char *templ = _("Attempt to eval bare function identifier token: "); + err.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 2); + strcpy(err.u.err, templ); + strcat(err.u.err, tfunc[n.u.fident].name); + return err; + } + case LOCATION: return n; + case FUNCALL: return full_eval_funcall(&n); + case EEK: return tcopy(n); + default: assert(0); + } + return n; +} diff --git a/src/common/sc.c b/src/common/sc.c index 0cbb141..33f1eec 100644 --- a/src/common/sc.c +++ b/src/common/sc.c @@ -17,8 +17,8 @@ #include #include - #include "eval.h" +#include "parser.h" #include "main.h" #include "sheet.h" #include "sc.h" @@ -224,7 +224,7 @@ const char *loadsc(Sheet *sheet, const char *name) width=strlen(buf); if (width>0 && buf[width-1]=='\n') buf[width-1]='\0'; /*}}}*/ - if (buf[0] && buf[0]!='#') + if (buf[0] && buf[0] != '#') { if (strncmp(buf,"format ",7)==0) /* format col width precision whoknows */ /*{{{*/ { @@ -241,28 +241,40 @@ const char *loadsc(Sheet *sheet, const char *name) setwidth(sheet, col, 0, colwidth); } /*}}}*/ - else if (strncmp(buf,"leftstring ",11)==0 || strncmp(buf,"rightstring ",12)==0) /* rightstring/leftstring cell = "string" */ /*{{{*/ + else if (strncmp(buf, "leftstring ", 11) == 0 + || strncmp(buf, "rightstring ", 12) == 0) /* rightstring/leftstring cell = "string" */ /*{{{*/ { int x,y; const char *s; Token **contents; - if (strncmp(buf,"leftstring ",11)==0) s=buf+11; else s=buf+12; - x=*s++-'A'; if (*s>='A' && *s<='Z') x=x*26+(*s++-'A'); - y=*s++-'0'; while (*s>='0' && *s<='9') y=10*y+(*s++-'0'); + if (strncmp(buf, "leftstring ", 11) == 0) s = buf+11; else s = buf+12; + x = *s++ - 'A'; + if (*s >= 'A' && *s <= 'Z' ) x = x*26 + (*s++ - 'A'); + y = *s++ - '0'; + while (*s >= '0' && *s <= '9') y = 10*y + (*s++-'0'); s+=3; - contents=scan(&s); - if (contents==(Token**)0) + contents = scan(&s); + if (contents == EMPTY_TVEC) { tvecfree(contents); - sprintf(errbuf,_("Expression syntax error in line %d"),line); - err=errbuf; + sprintf(errbuf, _("Expression syntax error in line %d"), line); + err = errbuf; goto eek; } tmp[X] = x; tmp[Y] = y; tmp[Z] = 0; cell = initcellofsheet(sheet, tmp); - cell->adjust = strncmp(buf,"leftstring ",11) ? RIGHT : LEFT; - cell->contents[BASE] = contents; + cell->adjust = strncmp(buf, "leftstring ", 11) ? RIGHT : LEFT; + cell->tok[BASE_CONT] = eval_safe(contents, LITERAL); + tvecfree(contents); + if (cell->tok[BASE_CONT].type == EEK) + { + sprintf(errbuf, _("Parse error in line %d: %s"), + line, cell->tok[BASE_CONT].u.err); + tfree(&(cell->tok[BASE_CONT])); + err = errbuf; + goto eek; + } } /*}}}*/ else if (strncmp(buf,"let ",4)==0) /* let cell = expression */ /*{{{*/ @@ -277,22 +289,22 @@ const char *loadsc(Sheet *sheet, const char *name) x=*s++-'A'; if (*s>='A' && *s<='Z') x=x*26+(*s++-'A'); y=*s++-'0'; while (*s>='0' && *s<='9') y=10*y+(*s++-'0'); tmp[X] = x; tmp[Y] = y; tmp[Z] = 0; - if (getcont(CELL_ATC(sheet,x,y,0),BASE)==(Token**)0) + if (gettok(CELL_ATC(sheet,x,y,0), BASE_CONT).type == EMPTY) { - s+=3; - s2t_s=s; s2t_t=newbuf; - if (s2t_term(sheet)==0) + s += 3; + s2t_s = s; s2t_t = newbuf; + if (s2t_term(sheet) == 0) { - *s2t_t='\0'; - if (err==(const char*)0) + *s2t_t = '\0'; + if (err == (const char*)0) { - sprintf(errbuf,_("Unimplemented SC feature in line %d"),line); - err=errbuf; + sprintf(errbuf, _("Unimplemented SC feature in line %d"),line); + err = errbuf; } } - *s2t_t='\0'; - s=newbuf; - contents=scan(&s); + *s2t_t = '\0'; + s = newbuf; + contents = scan(&s); if (contents==(Token**)0) { tvecfree(contents); @@ -303,7 +315,16 @@ const char *loadsc(Sheet *sheet, const char *name) tmp[X] = x; tmp[Y] = y; tmp[Z] = 0; cell = initcellofsheet(sheet, tmp); cell->adjust = RIGHT; - cell->contents[BASE] = contents; + cell->tok[BASE_CONT] = eval_safe(contents, LITERAL); + tvecfree(contents); + if (cell->tok[BASE_CONT].type == EEK) + { + sprintf(errbuf, _("Parse error in line %d: %s"), + line, cell->tok[BASE_CONT].u.err); + tfree(&(cell->tok[BASE_CONT])); + err = errbuf; + goto eek; + } } } /*}}}*/ diff --git a/src/common/scanner.c b/src/common/scanner.c index 5953ecf..a9f6bd6 100644 --- a/src/common/scanner.c +++ b/src/common/scanner.c @@ -18,6 +18,7 @@ #include extern char *strdup(const char* s); extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */ +extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */ #include @@ -43,18 +44,6 @@ const char *Op_Name[] = [POW] = "^", [MOD] = "%" }; -/* identcode -- return number of identifier */ /*{{{*/ -int identcode(const char *s, size_t len) -{ - Tfunc *p; - int fident; - - for (p=tfunc,fident=0; p->name[0]!='\0' && (len!=strlen(p->name) || strncmp(s,p->name,len)); ++p,++fident); - if (p->name[0]=='\0') return -1; - else return fident; -} -/*}}}*/ - /* loc_in_box -- returns true if test is in the box determined by b and c */ bool loc_in_box(const Location test, const Location b, const Location c) @@ -67,10 +56,21 @@ bool loc_in_box(const Location test, return true; } +/* cleartoken - Initialize all of the memory of a token */ /*{{{*/ +void cleartoken(Token* tok) +{ + tok->type = EMPTY; + tok->u.location[0] = 0; + tok->u.location[1] = 0; + tok->u.location[2] = 0; +} + /* duperror - Sets tok to an error and strdups the message into place */ -void duperror(Token* tok, const char* erro) { +Token duperror(Token* tok, const char* erro) +{ tok->type = EEK; tok->u.err = strdup(erro); + return *tok; } /* charstring -- match quoted string and return token */ /*{{{*/ @@ -103,6 +103,7 @@ static Token *charstring(const char **s) else return (Token*)0; } /*}}}*/ + /* integer -- match an unsigned integer and return token */ /*{{{*/ static Token *integer(const char **s) { @@ -123,6 +124,7 @@ static Token *integer(const char **s) else { *s=r; return (Token*)0; } } /*}}}*/ + /* flt -- match a floating point number */ /*{{{*/ static Token *flt(const char **s) { @@ -130,11 +132,11 @@ static Token *flt(const char **s) const char *t; char *end; Token *n; - double x; + FltT x; /*}}}*/ t=*s; - x=strtod(t,&end); + x = STRTOFLT(t,&end); *s = end; if (t!=*s && dblfinite(x)==(const char*)0) { @@ -150,6 +152,7 @@ static Token *flt(const char **s) } } /*}}}*/ + /* op -- match an op and return token */ /*{{{*/ static Token *op(const char **s) { @@ -194,7 +197,7 @@ static Token *ident(const char **s) begin=*s; ++(*s); while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s); result=malloc(sizeof(Token)); - if ((fident=identcode(begin,(size_t)(*s-begin)))==-1) + if ((fident = identcode(begin,(size_t)(*s-begin))) == NOT_A_FUNCTION) { result->type=LIDENT; result->u.lident=malloc((size_t)(*s-begin+1)); @@ -218,43 +221,43 @@ Token **scan(const char **s) /* variables */ /*{{{*/ Token **na,*n; const char *r; - int i,j; /*}}}*/ /* compute number of tokens */ /*{{{*/ - r=*s; - while (*r==' ') ++r; - for (i=0; *r!='\0'; ++i) + r = *s; + while (*r == ' ') ++r; + int i = 0; + for (; *r != '\0'; ++i) { const char *or; - or=r; - while (*r==' ') ++r; - n=charstring(&r); - if (n==(Token*)0) n=op(&r); - if (n==(Token*)0) n=integer(&r); - if (n==(Token*)0) n=flt(&r); - if (n==(Token*)0) n=ident(&r); - if (or==r) { *s=r; return (Token**)0; } + or = r; + while (*r == ' ') ++r; + n = charstring(&r); + if (n == NULLTOKEN) n = op(&r); + if (n == NULLTOKEN) n = integer(&r); + if (n == NULLTOKEN) n = flt(&r); + if (n == NULLTOKEN) n = ident(&r); + if (or == r) { *s = r; return EMPTY_TVEC; } } /*}}}*/ /* allocate token space */ /*{{{*/ - na=malloc(sizeof(Token*)*(i+1)); + na = malloc(sizeof(Token*)*(i+1)); /*}}}*/ /* store tokens */ /*{{{*/ - r=*s; + r = *s; while (*r==' ') ++r; - for (j=0; j 2) { printf("..Entering printtok; bufsize %d, field_width %d, qs %d, us %d, prec %d, verr %d\n", size, field_width, quote_strings, use_scientific, precision, verbose_error); } - cur = 0; - if (tok != NULLTOKEN) switch (tok->type) + if (size > 0 ) *dest = '\0'; + if (tok == NULLTOKEN || tok->type == EMPTY) return 0; + size_t cur = 0; + switch (tok->type) { - /* EMPTY */ /*{{{*/ - case EMPTY: if (size > 0) dest[cur] = '\0'; break; - /*}}}*/ /* STRING */ /*{{{*/ case STRING: { @@ -305,11 +305,11 @@ size_t printtok(char* dest, size_t size, size_t field_width, /* INT */ /*{{{*/ case INT: { - char buf[20]; + char buf[64]; size_t buflen; - buflen=sprintf(buf,"%ld",tok->u.integer); - assert(buflenu.integer); + assert(buflen < sizeof(buf)); (void)strncpy(dest+cur,buf,size-cur-1); cur+=buflen; break; @@ -323,19 +323,22 @@ size_t printtok(char* dest, size_t size, size_t field_width, size_t len; /*}}}*/ - len=sprintf(buf, use_scientific ? "%.*e" : "%.*f", - precision == -1 ? DBL_DIG-2 : precision, - tok->u.flt); + bool use_sci = use_scientific + || (int)ABSFLT(LOG10FLT(tok->u.flt)) > FLT_T_DIG; + + len = sprintf(buf, use_sci ? FLT_T_SCI_FMT : FLT_T_STD_FMT, + precision == -1 ? FLT_T_DIG-2 : precision, + tok->u.flt); assert(lenbuf && *p=='0' && *(p-1)!='.') { *p='\0'; --p; --len; } + while (p>buf && *p == '0' && *(p-1) != '.') { *p = '\0'; --p; --len; } } - p=buf+len; - while (*--p==' ') { *p='\0'; --len; } + p = buf+len; + while (*--p == ' ') { *p = '\0'; --len; } (void)strncpy(dest+cur,buf,size-cur-1); - cur+=len; + cur += len; break; } /*}}}*/ diff --git a/src/common/scanner.h b/src/common/scanner.h index fbf7553..f820d56 100644 --- a/src/common/scanner.h +++ b/src/common/scanner.h @@ -1,17 +1,21 @@ #ifndef SCANNER_H #define SCANNER_H +#include #include #include +#include "func.h" + #ifdef __cplusplus extern "C" { #endif +/* TO PRESERVE ABILITY TO READ OLD SAVE FILES, ONLY ADD ITEMS AT END */ typedef enum { EMPTY #ifndef __cplusplus - , STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, FUNCALL, EEK + , STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, EEK, FUNCALL #endif } Type; @@ -47,26 +51,107 @@ typedef enum { X=0, Y=1, Z=2, HYPER} Dimensions; bool loc_in_box(const Location test, const Location b, const Location c); -typedef struct Token_struc Token; - typedef struct { - int fident; + FunctionIdentifier fident; int argc; Token *argv; } FunctionCall; +#define FLT_LONG_DOUBLE +#ifdef FLT_LONG_DOUBLE +typedef long double FltT; + /* To switch the internal type used to store a floating value, it is + supposed to suffice to redefine all of the following macros: + */ +#define FLT_T_STD_FMT "%.*Lf" +#define FLT_T_SCI_FMT "%.*Le" +#define FLT_T_DIG LDBL_DIG +#define FLTEPS LDBL_EPSILON +#define FLTMX LDBL_MAX +#define STRTOFLT strtold +#define ABSFLT fabsl +#define LOGFLT logl +#define LOG2FLT log2l +#define LOG10FLT log10l +#define SQRTFLT sqrtl +#define POWFLT powl +#define FMODFLT fmodl +#define MODFFLT modfl +#define SINFLT sinl +#define COSFLT cosl +#define TANFLT tanl +#define SINHFLT sinhl +#define COSHFLT coshl +#define TANHFLT tanhl +#define ASINFLT asinl +#define ACOSFLT acosl +#define ATANFLT atanl +#define FLOORFLT floorl +#define CEILFLT ceill +#define ROUNDFLT rintl +#define TRUNCFLT truncl +#else +typedef double FltT; + /* To switch the internal type used to store a floating value, it is + supposed to suffice to redefine all of the following macros: + */ +#define FLT_T_STD_FMT "%.*f" +#define FLT_T_SCI_FMT "%.*e" +#define FLT_T_DIG DBL_DIG +#define FLTEPS DBL_EPSILON +#define FLTMX DBL_MAX +#define STRTOFLT strtod +#define ABSFLT fabs +#define LOGFLT log +#define LOG2FLT log2 +#define LOG10FLT log10 +#define SQRTFLT sqrt +#define POWFLT pow +#define FMODFLT fmod +#define MODFFLT modf +#define SINFLT sin +#define COSFLT cos +#define TANFLT tan +#define SINHFLT sinh +#define COSHFLT cosh +#define TANHFLT tanh +#define ASINFLT asin +#define ACOSFLT acos +#define ATANFLT atan +#define FLOORFLT floor +#define CEILFLT ceil +#define ROUNDFLT rint +#define TRUNCFLT trunc +#endif + +typedef long long IntT; + /* To switch the internal type used to store an integer value, it is + supposed to suffice to redefine all of the following macros and typedefs. + */ +typedef unsigned long long UIntT; +#define INT_T_FMT "%lld" +#define STRTOINT strtoll + /* This one depends on both the floating point and integer types */ +#ifdef FLT_LONG_DOUBLE +#define ROUNDFLTINT llrintl +#else +#define ROUNDFLTINT llrint +#endif + /* End of subtype definitions */ + +/* The main type defined here; should this be token.h ?*/ typedef struct Token_struc { Type type; union { char *string; - double flt; - long integer; + FltT flt; + IntT integer; Operator op; char *lident; - int fident; + FunctionIdentifier fident; Location location; FunctionCall funcall; char *err; @@ -75,13 +160,14 @@ typedef struct Token_struc #define NULLTOKEN ((Token*)0) #define EMPTY_TVEC ((Token**)0) +#define TOKISNUM(t) ((t).type == INT || (t).type == FLOAT || (t).type == EMPTY) -int identcode(const char *s, size_t len); -void duperror(Token* tok, const char* erro); +Token duperror(Token* tok, const char* erro); Token **scan(const char **s); +void cleartoken(Token* tok); size_t printtok(char *dest, size_t size, size_t field_width, - int quote_strings, int use_scientific, - int precision, int verbose_error, const Token *tok); + bool quote_strings, bool use_scientific, + int precision, bool verbose_error, const Token *tok); void print(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Token **n); #ifdef __cplusplus diff --git a/src/common/sheet.c b/src/common/sheet.c index 4d45e43..dfd5a3d 100644 --- a/src/common/sheet.c +++ b/src/common/sheet.c @@ -46,7 +46,8 @@ int max_eval; /*}}}*/ /* copycelltosheet -- copy a cell into a sheet*/ /*{{{*/ -void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to) +void copycelltosheet(const Cell *fromcell, Sheet *sheet2, + const Location to, LabelHandling lh) { assert(sheet2 != (Sheet*)0); Cell *tocell = safe_cell_at(sheet2, to); @@ -59,7 +60,7 @@ void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to) sheet2->changed = 1; Cell *alsotocell = initcellofsheet(sheet2, to); assert(tocell == NULLCELL || tocell == alsotocell); - copycell(alsotocell, fromcell); + copycell(alsotocell, fromcell, lh); } /*}}}*/ } @@ -91,16 +92,14 @@ static void dump_cell(Sheet *sheet, int x, int y, int z) return; } printf("TEADUMP of &(%d,%d,%d):\n", x, y, z); - print(buf, sizeof(buf), 0, 1, 0, -1, c->contents[BASE]); - printf(" Base expr: %s.\n", buf); - print(buf, sizeof(buf), 0, 1, 0, -1, c->contents[ITERATIVE]); - printf(" Update expr: %s.\n", buf); - if (c->label == (char *)0) printf("\n No label.\n"); + for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv) + { + printf(" %s: ", TokVariety_Name[tv]); + printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, c->tok + tv); + printf("%s.\n", buf); + } + if (c->label == NULL) printf("\n No label.\n"); else printf("\n Label: %s.\n", c->label); - printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, &(c->value)); - printf(" Current value: %s.\n", buf); - printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, &(c->resvalue)); - printf(" Stored result value: %s.\n Adjustment: ", buf); switch (c->adjust) { case LEFT: printf("LEFT\n"); break; case RIGHT: printf("RIGHT\n"); break; @@ -108,6 +107,8 @@ static void dump_cell(Sheet *sheet, int x, int y, int z) case AUTOADJUST: printf("AUTO\n"); break; } printf(" Precision: %d\n Attributes: ", c->precision); + for (ColorAspect ca = FOREGROUND; ca < NUM_COLOR_ASPECTS; ++ca) + printf(" %s color: %d\n", ColorAspect_Name[ca], c->aspect[ca]); if (c->updated) printf("updated "); if (c->shadowed) printf("shadowed "); if (c->scientific) printf("scientific "); @@ -154,6 +155,30 @@ static void swapblock(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int sheet2->changed=1; } /*}}}*/ + +/* signum -- -1 if < 0, 0 if 0, 1 if > 0 */ +static int signum(int x) { + return (x > 0) - (x < 0); +} +/* cmpflt -- -1 if <, 0 if =, 1 if > */ +static int cmpflt(FltT x, FltT y) { + return (x > y) - (x < y); +} +/* cmpflint -- -1 if <, 0 if =, 1 if > */ +static int cmpflint(FltT x, IntT y) { + return (x > y) - (x < y); +} +/* cmpint -- -1 if <, 0 if =, 1 if > */ +static int cmpint(IntT x, IntT y) { + return (x > y) - (x < y); +} +/* cmpintfl -- -1 if <, 0 if =, 1 if > */ +static int cmpintfl(IntT x, FltT y) { + return (x > y) - (x < y); +} + + +#define NOT_COMPARABLE 2 /* cmpcell -- compare to cells with given order flags */ /*{{{*/ /* Notes */ /*{{{*/ /* @@ -164,73 +189,42 @@ second. A result of 2 means they are not comparable. /*}}}*/ static int cmpcell(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2, int sortkey) { - Cell *leftcell, *rightcell; - assert(sheet1!=(Sheet*)0); - assert(sheet2!=(Sheet*)0); + int ascending = (sortkey & ASCENDING) ? 1 : -1; + Token leftval = gettok(safe_cell_atc(sheet1, x1, y1, z1), CURR_VAL); + Token rightval = gettok(safe_cell_atc(sheet2, x2, y2, z2), CURR_VAL); /* empty cells are smaller than any non-empty cell */ /*{{{*/ - if (!CELL_IS_GOODC(sheet1,x1,y1,z1) || CELL_ATC(sheet1,x1,y1,z1)->value.type==EMPTY) - { - if (!CELL_IS_GOODC(sheet2,x2,y2,z2) || CELL_ATC(sheet2,x2,y2,z2)->value.type==EMPTY) return 0; - else return (sortkey&ASCENDING ? -1 : 1); - } - if (!CELL_IS_GOODC(sheet2,x2,y2,z2) || CELL_ATC(sheet2,x2,y2,z2)->value.type==EMPTY) return (sortkey&ASCENDING ? 1 : -1); + if (leftval.type == EMPTY) + if (rightval.type == EMPTY) return 0; + else return -ascending; + if (rightval.type == EMPTY) return ascending; /*}}}*/ - leftcell = CELL_ATC(sheet1,x1,y1,z1); - rightcell = CELL_ATC(sheet2,x2,y2,z2); - switch (leftcell->value.type) + switch (leftval.type) { /* STRING */ /*{{{*/ case STRING: - { - if (rightcell->value.type==STRING) - { - int r; - - r=strcmp(leftcell->value.u.string, rightcell->value.u.string); - if (r<0) return (sortkey&ASCENDING ? -1 : 1); - else if (r==0) return 0; - else return (sortkey&ASCENDING ? 1 : -1); - } - return 2; - } + if (rightval.type == STRING) + return signum(strcmp(leftval.u.string, rightval.u.string)) * ascending; + return NOT_COMPARABLE; /*}}}*/ /* FLOAT */ /*{{{*/ case FLOAT: - { - if (rightcell->value.type==FLOAT) - { - if (leftcell->value.u.fltvalue.u.flt) return (sortkey&ASCENDING ? -1 : 1); - else if (leftcell->value.u.flt==rightcell->value.u.flt) return 0; - else return (sortkey&ASCENDING ? 1 : -1); - } - if (rightcell->value.type==INT) - { - if (leftcell->value.u.fltvalue.u.integer) return (sortkey&ASCENDING ? -1 : 1); - else if (leftcell->value.u.flt==rightcell->value.u.integer) return 0; - else return (sortkey&ASCENDING ? 1 : -1); - } - return 2; - } + if (rightval.type == FLOAT) + return cmpflt(leftval.u.flt, rightval.u.flt)*ascending; + if (rightval.type == INT) + return cmpflint(leftval.u.flt, rightval.u.integer)*ascending; + return NOT_COMPARABLE; /*}}}*/ /* INT */ /*{{{*/ case INT: { - if (rightcell->value.type==INT) - { - if (leftcell->value.u.integervalue.u.integer) return (sortkey&ASCENDING ? -1 : 1); - else if (leftcell->value.u.integer==rightcell->value.u.integer) return 0; - else return (sortkey&ASCENDING ? 1 : -1); - } - if (rightcell->value.type==FLOAT) - { - if (leftcell->value.u.integervalue.u.flt) return (sortkey&ASCENDING ? -1 : 1); - else if (leftcell->value.u.integer==rightcell->value.u.flt) return 0; - else return (sortkey&ASCENDING ? 1 : -1); - } - return 2; + if (rightval.type == INT) + return cmpint(leftval.u.integer, rightval.u.integer)*ascending; + if (rightval.type == FLOAT) + return cmpintfl(leftval.u.integer, rightval.u.flt)*ascending; + return NOT_COMPARABLE; } /*}}}*/ - default: return 2; + default: return NOT_COMPARABLE; } } /*}}}*/ @@ -334,6 +328,17 @@ Cell *initcellofsheet(Sheet *sheet, const Location at) even in the face of various edge conditions. */ +Cell *safe_cell_atc(Sheet *sheet, int x, int y, int z) +{ + if (sheet == (Sheet*)0) return NULLCELL; + if (sheet->sheet == (Cell **)0) return NULLCELL; + if (!LOC_WITHINC(sheet,x,y,z)) return NULLCELL; + return CELL_ATC(sheet,x,y,z); +} +/* safe_cell_at -- returns pointer to the cell at the given location, + even in the face of various edge conditions. +*/ + Cell *safe_cell_at(Sheet *sheet, const Location at) { if (sheet == (Sheet*)0) return NULLCELL; @@ -511,21 +516,21 @@ int cellwidth(Sheet *sheet, const Location at) /*}}}*/ /* putcont -- assign new contents */ /*{{{*/ -void putcont(Sheet *sheet, const Location at, Token **t, ContentVariety v) +void puttok(Sheet *sheet, const Location at, Token t, TokVariety v) { Cell *cell; assert(sheet != (Sheet*)0); sheet->changed = 1; cell = initcellofsheet(sheet, at); - tvecfree(cell->contents[v]); - cell->contents[v] = t; + tfree(&(cell->tok[v])); + cell->tok[v] = t; redraw_cell(sheet, at); } /*}}}*/ -/* getvalue -- get tcopy()ed value */ /*{{{*/ -Token getvalue(Sheet *sheet, const Location at) +/* recompvalue -- get tcopy()ed value */ /*{{{*/ +Token recompvalue(Sheet *sheet, const Location at) { /* variables */ /*{{{*/ Token result; @@ -551,19 +556,13 @@ Token getvalue(Sheet *sheet, const Location at) depending on this call to update the current value */ cell = CELL_AT(sheet, at); - if (!upd_clock && getcont(cell,2) == EMPTY_TVEC) return result; + if (!upd_clock && gettok(cell, CONTINGENT).type == EMPTY) return result; /* update value of this cell if needed and return it */ /*{{{*/ orig_upd_clock = upd_clock; if (cell->ignored) { - /* variables */ /*{{{*/ - Token oldvalue; - /*}}}*/ - - oldvalue = cell->value; + tfree(cell->tok + CURR_VAL); cell->updated = 1; - cell->value.type = EMPTY; - tfree(&oldvalue); } else if (cell->updated == 0) { @@ -583,24 +582,24 @@ Token getvalue(Sheet *sheet, const Location at) if (cell->clock_t1 == 0) { cell->updated = 1; - oldvalue = cell->value; + oldvalue = gettok(cell, CURR_VAL); upd_clock = 0; - cell->value = eval_safe(getcont(cell, CONTINGENT), FULL); + cell->tok[CURR_VAL] = evaltoken(gettok(cell, CONTINGENT), FULL); tfree(&oldvalue); } else if (upd_clock) { cell->updated = 1; upd_clock = 0; - oldvalue = cell->resvalue; - cell->resvalue = eval_safe(getcont(cell, CONTINGENT), FULL); + oldvalue = gettok(cell, RES_VAL); + cell->tok[RES_VAL] = evaltoken(gettok(cell, CONTINGENT), FULL); tfree(&oldvalue); } upd_sheet = old_sheet; LOCATION_GETS(upd_l, old_l); max_eval = old_max_eval; } - if (!orig_upd_clock) result = tcopy(cell->value); + if (!orig_upd_clock) result = tcopy(cell->tok[CURR_VAL]); /*}}}*/ return result; } @@ -638,15 +637,15 @@ void update(Sheet *sheet) for (ALL_LOCS_IN_SHEET(sheet,w)) { upd_clock = 1; - getvalue(sheet, w); + recompvalue(sheet, w); } for (ALL_CELLS_IN_SHEET(sheet,i,cell)) { if (cell && cell->clock_t1) { - tfree(&(cell->value)); - cell->value = cell->resvalue; - cell->resvalue.type = EMPTY; + tfree(&(cell->tok[CURR_VAL])); + cell->tok[CURR_VAL] = cell->tok[RES_VAL];; + cell->tok[RES_VAL].type = EMPTY; cell->clock_t1 = 0; } } @@ -663,25 +662,20 @@ char *geterror(Sheet *sheet, const Location at) Token v; assert(sheet!=(Sheet*)0); - if ((v = getvalue(sheet,at)).type != EEK) + if ((v = recompvalue(sheet,at)).type != EEK) { tfree(&v); - return (char*)0; - } - else - { - return (v.u.err); + return NULL; } + else return (v.u.err); } /*}}}*/ /* printvalue -- get ASCII representation of value */ /*{{{*/ void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, const Location at) { - Token t; - assert(sheet != (Sheet*)0); - t = getvalue(sheet, at); + Token t = recompvalue(sheet, at); printtok(s, size, chars, quote, scientific, precision, 0, &t); tfree(&t); } @@ -842,91 +836,46 @@ Token findlabel(Sheet *sheet, const char *label) } /*}}}*/ +/* search and replace for labels in a token */ + +static void relabel_token(Token *tok, const char *oldl, const char *newl) +{ + switch (tok->type) + { + case LIDENT: + if (strcmp(tok->u.lident, oldl) == 0) + { + tfree(tok); + tok->type = LIDENT; + tok->u.lident = strdup(newl); + } + break; + case FUNCALL: + for (int ai = 0; ai < tok->u.funcall.argc; ++ai) + relabel_token((tok->u.funcall.argv) + ai, oldl, newl); + break; + default: + break; + } +} + /* relabel -- search and replace for labels */ /*{{{*/ void relabel(Sheet *sheet, const Location at, const char *oldlabel, const char *newlabel) { - /* variables */ /*{{{*/ - Token **run; - ContentVariety v; - Cell *cell; - /*}}}*/ - /* asserts */ /*{{{*/ assert(oldlabel!=(const char*)0); assert(newlabel!=(const char*)0); /*}}}*/ + if (!LOC_WITHIN(sheet, at)) return; - cell = CELL_AT(sheet, at); + Cell *cell = CELL_AT(sheet, at); if (cell != NULLCELL) - for (v = BASE; v <= ITERATIVE; ++v) - if (cell->contents[v] != EMPTY_TVEC) - for (run = cell->contents[v]; *run != NULLTOKEN; ++run) - if ((*run)->type==LIDENT && strcmp((*run)->u.lident, oldlabel)==0) - { - free((*run)->u.lident); - (*run)->u.lident = strdup(newlabel); - } - cachelabels(sheet); - forceupdate(sheet); + for (TokVariety v = BASE_CONT; v <= MAX_PERSIST_TV; ++v) + relabel_token(cell->tok + v, oldlabel, newlabel); } /*}}}*/ -/* savexdr -- save a spread sheet in XDR */ /*{{{*/ -const char *savexdr(Sheet *sheet, const char *name, unsigned int *count) -{ - /* variables */ /*{{{*/ - FILE *fp; - XDR xdrs; - int x,y,z; - /*}}}*/ - - *count=0; - if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno); - xdrstdio_create(&xdrs,fp,XDR_ENCODE); - if (!xdr_magic(&xdrs)) - { - xdr_destroy(&xdrs); - (void)fclose(fp); - return strerror(errno); - } - for (x=sheet->dimx-1; x>=0; --x) for (z=sheet->dimz-1; z>=0; --z) - { - int width; - int u; - - width=columnwidth(sheet,x,z); - if (width!=DEF_COLUMNWIDTH) - { - u=0; - if (xdr_int(&xdrs,&u)==0 || xdr_column(&xdrs,&x,&z,&width)==0) - { - xdr_destroy(&xdrs); - (void)fclose(fp); - return strerror(errno); - } - } - for (y=sheet->dimy-1; y>=0; --y) - { - if (CELL_ATC(sheet,x,y,z)!=NULLCELL) - { - u=1; - if (xdr_int(&xdrs,&u)==0 || xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0 || xdr_cell(&xdrs,CELL_ATC(sheet,x,y,z))==0) - { - xdr_destroy(&xdrs); - (void)fclose(fp); - return strerror(errno); - } - ++*count; - } - } - } - xdr_destroy(&xdrs); - if (fclose(fp)==EOF) return strerror(errno); - sheet->changed=0; - return (const char*)0; -} -/*}}}*/ /* savetbl -- save as tbl tyble */ /*{{{*/ const char *savetbl(Sheet *sheet, const char *name, int body, const Location beg, const Location end, @@ -1131,73 +1080,6 @@ const char *savecsv(Sheet *sheet, const char *name, char sep, /* variables */ /*{{{*/ FILE *fp; Location w; - Cell *cw; - /*}}}*/ - - /* asserts */ /*{{{*/ - assert(sheet!=(Sheet*)0); - assert(name!=(const char*)0); - /*}}}*/ - *count=0; - w[X] = beg[X]; - for (w[Z]=beg[Z]; w[Z]<=end[Z]; ++(w[Z])) - for (w[Y]=beg[Y]; w[Y]<=end[Y]; ++(w[Y])) - if (shadowed(CELL_AT(sheet,w))) return _("Shadowed cells in first column"); - if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno); - for (w[Z]=beg[Z]; w[Z]<=end[Z]; ++(w[Z])) - { - for (w[Y]=beg[Y]; w[Y]<=end[Y]; ++(w[Y])) - { - for (w[X]=beg[X]; w[X]<=end[X]; ++(w[X])) - { - if (w[X]>beg[X]) if (fputc_close(sep,fp)==EOF) return strerror(errno); - cw = CELL_AT(sheet,w); - if (cw != NULLCELL) - { - char *buf,*s; - - buf=malloc(255*UTF8SZ+1); - printvalue(buf, 255*UTF8SZ+1, 255, 0, getscientific(cw), - getprecision(cw), sheet, w); - if (cw->value.type == STRING && fputc_close('"',fp)==EOF) - { - free(buf); - return strerror(errno); - } - for (s=buf; *s; ++s) - { - if (fputc_close(*s,fp)==EOF || (*s=='"' && fputc_close(*s,fp)==EOF)) - { - free(buf); - return strerror(errno); - } - } - free(buf); - if (cw->value.type==STRING && fputc_close('"',fp)==EOF) return strerror(errno); - } - ++*count; - } - if (fputc_close('\n',fp)==EOF) return strerror(errno); - } - if (w[Z] < end[Z] && fputs_close("\f",fp)==EOF) return strerror(errno); - } - if (fclose(fp)==EOF) return strerror(errno); - return (const char*)0; -} -/*}}}*/ - -static const char *saveport_contentleader[] = - { [BASE] = ":", [ITERATIVE] = "\\\n" - }; - -/* saveport -- save as portable text */ /*{{{*/ -const char *saveport(Sheet *sheet, const char *name, unsigned int *count) -{ - /* variables */ /*{{{*/ - FILE *fp; - int x,y,z; - Cell *cell; - ContentVariety v; /*}}}*/ /* asserts */ /*{{{*/ @@ -1205,17 +1087,84 @@ const char *saveport(Sheet *sheet, const char *name, unsigned int *count) assert(name != (const char*)0); /*}}}*/ *count = 0; - if ((fp=fopen(name,"w")) == (FILE*)0) return strerror(errno); - fprintf(fp,"# This is a work sheet generated with teapot %s.\n",VERSION); - for (z=sheet->dimz-1; z>=0; --z) + w[X] = beg[X]; + for (w[Z] = beg[Z]; w[Z] <= end[Z]; ++(w[Z])) + for (w[Y] = beg[Y]; w[Y] <= end[Y]; ++(w[Y])) + if (shadowed(CELL_AT(sheet,w))) return _("Shadowed cells in first column"); + if ((fp = fopen(name,"w")) == (FILE*)0) return strerror(errno); + for (w[Z]=beg[Z]; w[Z]<=end[Z]; ++(w[Z])) { - for (y=sheet->dimy-1; y>=0; --y) + for (w[Y]=beg[Y]; w[Y]<=end[Y]; ++(w[Y])) { - for (x=sheet->dimx-1; x>=0; --x) + for (w[X]=beg[X]; w[X]<=end[X]; ++(w[X])) + { + if (w[X] > beg[X]) + if (fputc_close(sep,fp) == EOF) return strerror(errno); + Cell *cw = CELL_AT(sheet,w); + if (cw != NULLCELL) + { + static const int bufsz = 511*UTF8SZ + 1; + char *buf = malloc(bufsz); + + printvalue(buf, bufsz, 255, 0, getscientific(cw), + getprecision(cw), sheet, w); + bool needquote = !transparent(cw) + && (strlen(buf) != strcspn(buf, "\",\n\r")); + if (needquote && fputc_close('"',fp) == EOF) + { + free(buf); + return strerror(errno); + } + for (const char *s = buf; *s; ++s) + { + if (fputc_close(*s,fp) == EOF + || (*s=='"' && needquote && fputc_close(*s,fp) == EOF)) + { + free(buf); + return strerror(errno); + } + } + free(buf); + if (needquote && fputc_close('"', fp) == EOF) return strerror(errno); + } + ++*count; + } + if (fputc_close('\n', fp) == EOF) return strerror(errno); + } + if (w[Z] < end[Z] && fputc_close('\f', fp) == EOF) return strerror(errno); + } + if (fclose(fp) == EOF) return strerror(errno); + return (const char*)0; +} +/*}}}*/ + +static const char *saveport_contentleader[] = + { [BASE_CONT] = ":", [ITER_CONT] = "\\\n", [ATTR_REF] = "\\\n" + }; + +/* saveport -- save as portable text */ /*{{{*/ +const char *saveport(Sheet *sheet, const char *name, unsigned int *count) +{ + /* variables */ /*{{{*/ + FILE *fp; + /*}}}*/ + + /* asserts */ /*{{{*/ + assert(sheet != (Sheet*)0); + assert(name != (const char*)0); + /*}}}*/ + *count = 0; + if ((fp = fopen(name,"w")) == (FILE*)0) return strerror(errno); + fprintf(fp,"# This is a work sheet generated with teapot %s.\n",VERSION); + for (int z = sheet->dimz - 1; z >= 0; --z) + { + for (int y = sheet->dimy - 1; y >= 0; --y) + { + for (int x = sheet->dimx - 1; x >= 0; --x) { if (y == 0 && columnwidth(sheet,x,z) != DEF_COLUMNWIDTH) fprintf(fp,"W%d %d %d\n",x,z,columnwidth(sheet,x,z)); - cell = CELL_ATC(sheet,x,y,z); + Cell *cell = CELL_ATC(sheet,x,y,z); if (cell != NULLCELL) { fprintf(fp,"C%d %d %d ",x,y,z); @@ -1224,23 +1173,36 @@ const char *saveport(Sheet *sheet, const char *name, unsigned int *count) if (cell->label) fprintf(fp,"L%s ", cell->label); if (cell->precision != -1) fprintf(fp,"P%d ", cell->precision); + bool needscolor = false; + for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a) + if (cell->aspect[a] != DefaultCN[a]) + { needscolor = true; break; } + if (needscolor) + { + fprintf(fp, "H%d ", NUM_COLOR_ASPECTS); + for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a) + fprintf(fp, "%d ", (int)cell->aspect[a]); + } if (cell->shadowed) fprintf(fp,"S "); if (cell->bold) fprintf(fp,"B "); if (cell->underline) fprintf(fp,"U "); if (cell->scientific != DEF_SCIENTIFIC) fprintf(fp,"E "); - if (cell->locked) fprintf(fp,"C "); + if (cell->locked) fprintf(fp, "K "); if (cell->transparent) fprintf(fp,"T "); - for (v = BASE; v <= ITERATIVE; ++v) - if (cell->contents[v]) - { - char buf[4096]; - - if (fputs_close(saveport_contentleader[v], fp) == EOF) - return strerror(errno); - print(buf, sizeof(buf), 0, 1, cell->scientific, cell->precision, - cell->contents[v]); - if (fputs_close(buf, fp) == EOF) return strerror(errno); - } + if (cell->ignored) fprintf(fp, "I "); + TokVariety lastv = MAX_PERSIST_TV; + while (lastv > BASE_CONT && cell->tok[lastv].type == EMPTY) --lastv; + for (TokVariety v = BASE_CONT; v <= lastv; ++v) + { + if (v == BASE_CONT && cell->tok[v].type == EMPTY) continue; + char buf[4096]; + if (fputs_close(saveport_contentleader[v], fp) == EOF) + return strerror(errno); + printtok(buf, sizeof(buf), 0, 1, cell->scientific, + cell->precision, 0, cell->tok + v); + if (fputs_close(buf, fp) == EOF) return strerror(errno); + } + if (fputc_close('\n', fp) == EOF) return strerror(errno); } } } @@ -1274,16 +1236,16 @@ const char *loadport(Sheet *sheet, const char *name) { /* remove nl */ /*{{{*/ width = strlen(buf); - if (width>0 && buf[width-1]=='\n') buf[--width] = '\0'; + if (width > 0 && buf[width-1] == '\n') buf[--width] = '\0'; /*}}}*/ switch (buf[0]) { /* C -- parse cell */ /*{{{*/ case 'C': { - ContentVariety cv = BASE, nextcv = BASE; + TokVariety rv = BASE_CONT, nextrv = BASE_CONT; - if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextcv; } + if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextrv; } initcellcontents(&loaded); /* parse x y and z */ /*{{{*/ os=ns=buf+1; @@ -1306,7 +1268,7 @@ const char *loadport(Sheet *sheet, const char *name) while (*ns==' ') ++ns; os=ns; loc[Z] = posnumber(os,&ns); - if (os==ns) + if (os == ns) { sprintf(errbuf,_("Parse error for z position in line %d"),line); err=errbuf; @@ -1360,6 +1322,34 @@ const char *loadport(Sheet *sheet, const char *name) break; } /*}}}*/ + /* H -- hue (colors) */ /*{{{*/ + case 'H': + { + os = ++ns; + size_t na = posnumber(os, &ns); + if (os == ns || na > NUM_COLOR_ASPECTS) + { + sprintf(errbuf, _("Parse error for number of hues in line %d"), + line); + err = errbuf; + goto eek; + } + for (size_t a = 0; a < na; ++a) + { + while (*ns && (*ns == ' ')) { ++ns; } + os = ns; + loaded.aspect[a] = posnumber(os, &ns); + if (os == ns) + { + sprintf(errbuf, _("Parse error in hue %d on line %d"), + a, line); + err = errbuf; + goto eek; + } + } + break; + } + /*}}}*/ /* S -- shadowed */ /*{{{*/ case 'S': { @@ -1413,8 +1403,9 @@ const char *loadport(Sheet *sheet, const char *name) break; } /*}}}*/ - /* O -- locked */ /*{{{*/ - case 'O': + /* K (formerly C) -- locked */ /*{{{*/ + case 'C': + case 'K': { ++ns; loaded.locked=1; @@ -1451,26 +1442,37 @@ const char *loadport(Sheet *sheet, const char *name) /* convert remaining string(s) into token sequence */ /*{{{*/ while (true) { - loaded.contents[cv] = scan(&ns); - if (loaded.contents[cv]==(Token**)0) + Token **t = scan(&ns); + if (t == EMPTY_TVEC) { sprintf(errbuf,_("Expression syntax error in line %d"),line); err=errbuf; goto eek; } - if (nextcv == cv) break; - cv = nextcv; + Token pt = eval_safe(t, LITERAL); + tvecfree(t); + if (pt.type == EEK) + { + sprintf(errbuf, _("Parse error in line %d: %s"), + line, pt.u.err); + err = errbuf; + tfree(&pt); + goto eek; + } + loaded.tok[rv] = pt; + if (nextrv == rv) break; + rv = nextrv; if (fgets(buf, sizeof(buf), fp) == (char*)0) break; ++line; width = strlen(buf); if (width>0 && buf[width-1]=='\n') buf[width-1]='\0'; /* More content? */ - if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextcv; } + if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextrv; } ns = buf; } /*}}}*/ - copycelltosheet(&loaded, sheet, loc); + copycelltosheet(&loaded, sheet, loc, PRESERVE_LABEL); break; } /*}}}*/ @@ -1534,96 +1536,6 @@ const char *loadport(Sheet *sheet, const char *name) } /*}}}*/ -/* loadxdr -- load a spread sheet in XDR */ /*{{{*/ -const char *loadxdr(Sheet *sheet, const char *name) -{ - /* variables */ /*{{{*/ - FILE *fp; - XDR xdrs; - Location w; - int width; - int u; - int olderror; - Cell *nc; - /*}}}*/ - - if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno); - xdrstdio_create(&xdrs,fp,XDR_DECODE); - if (!xdr_magic(&xdrs)) - { -#if 0 - xdr_destroy(&xdrs); - fclose(fp); - return _("This is not a teapot worksheet in XDR format"); -#else - xdr_destroy(&xdrs); - rewind(fp); - xdrstdio_create(&xdrs,fp,XDR_DECODE); -#endif - } - freesheet(sheet,0); - while (xdr_int(&xdrs,&u)) switch (u) - { - /* 0 -- column width element */ /*{{{*/ - case 0: - { - if (xdr_column(&xdrs,&(w[X]),&(w[Z]),&width)==0) - { - olderror=errno; - xdr_destroy(&xdrs); - (void)fclose(fp); - return strerror(olderror); - } - setwidth(sheet,w[X],w[Z],width); - break; - } - /*}}}*/ - /* 1 -- cell element */ /*{{{*/ - case 1: - { - if (xdr_int(&xdrs,&(w[X]))==0 || - xdr_int(&xdrs,&(w[Y]))==0 || - xdr_int(&xdrs,&(w[Z]))==0) - { - olderror=errno; - xdr_destroy(&xdrs); - (void)fclose(fp); - return strerror(olderror); - } - nc = initcellofsheet(sheet, w); - if (xdr_cell(&xdrs, nc)==0) - { - freecellofsheet(sheet, w); - olderror=errno; - xdr_destroy(&xdrs); - (void)fclose(fp); - return strerror(olderror); - } - break; - } - /*}}}*/ - /* default -- should not happen */ /*{{{*/ - default: - { - xdr_destroy(&xdrs); - fclose(fp); - sheet->changed=0; - cachelabels(sheet); - forceupdate(sheet); - return _("Invalid record, loading aborted"); - } - /*}}}*/ - } - xdr_destroy(&xdrs); - if (fclose(fp)==EOF) return strerror(errno); - sheet->changed=0; - cachelabels(sheet); - forceupdate(sheet); - redraw_sheet(sheet); - return (const char*)0; -} -/*}}}*/ - /* loadcsv -- load/merge CSVs */ /*{{{*/ const char *loadcsv(Sheet *sheet, const char *name) { @@ -1634,8 +1546,8 @@ const char *loadcsv(Sheet *sheet, const char *name) Location where; char ln[4096]; const char *str; - double value; - long lvalue; + FltT value; + IntT lvalue; int separator = 0; /*}}}*/ @@ -1661,51 +1573,50 @@ const char *loadcsv(Sheet *sheet, const char *name) where[X] = sheet->cur[X]; do { - t=malloc(2*sizeof(Token*)); - t[0]=malloc(sizeof(Token)); - t[1]=(Token*)0; - lvalue=csv_long(s,&cend); - if (s!=cend) /* ok, it is a integer */ /*{{{*/ + Token t; + t.type = EMPTY; + + lvalue = csv_long(s, &cend); + if (s != cend) /* ok, it is a integer */ /*{{{*/ { - t[0]->type=INT; - t[0]->u.integer=lvalue; - putcont(sheet, where, t, BASE); + t.type = INT; + t.u.integer = lvalue; + puttok(sheet, where, t, BASE_CONT); } /*}}}*/ else { - value=csv_double(s,&cend); - if (s!=cend) /* ok, it is a double */ /*{{{*/ + value = csv_double(s, &cend); + if (s != cend) /* ok, it is a double */ /*{{{*/ { - t[0]->type=FLOAT; - t[0]->u.flt=value; - putcont(sheet, where, t, BASE); + t.type = FLOAT; + t.u.flt = value; + puttok(sheet, where, t, BASE_CONT); } /*}}}*/ else { - str=csv_string(s,&cend); - if (s!=cend) /* ok, it is a string */ /*{{{*/ + str = csv_string(s, &cend); + if (s != cend) /* ok, it is a string */ /*{{{*/ { - t[0]->type=STRING; - t[0]->u.string=strdup(str); - putcont(sheet, where, t, BASE); + t.type = STRING; + t.u.string = strdup(str); + puttok(sheet, where, t, BASE_CONT); } /*}}}*/ else { - tvecfree(t); csv_separator(s,&cend); - while (s==cend && *s && *s!='\n') + while (s == cend && *s && *s!='\n') { err=_("unknown values ignored"); - csv_separator(++s,&cend); + csv_separator(++s, &cend); } /* else it is nothing, which does not need to be stored :) */ } } } - } while (s!=cend ? s=cend,++(where[X]),1 : 0); + } while (s != cend ? s = cend, ++(where[X]), 1 : 0); } fclose(fp); return err; @@ -1884,7 +1795,8 @@ void moveblock(Sheet *sheet, const Location beg, const Location end, get[X] != to[X]; get[X] += dir[X], go[X] += dir[X]) { if (!LOC_WITHIN(sheet, get)) freecellofsheet(sheet, go); - else if (copy) { copycelltosheet(CELL_AT(sheet, get), sheet, go); } + else if (copy) + copycelltosheet(CELL_AT(sheet, get), sheet, go, ALTER_LABEL); else { resize(sheet, go[X], go[Y], go[Z]); @@ -1916,7 +1828,8 @@ const char *sortblock(Sheet *sheet, const Location beg, const Location end, int x,y,z; int incx=0,incy=0,incz=0; int distx,disty,distz; - int i,r=-3,norel,work; + int i,r=-3,work; + bool norel = false; /*}}}*/ /* unpack corners */ @@ -1954,8 +1867,8 @@ const char *sortblock(Sheet *sheet, const Location beg, const Location end, { for (i=0; itype=FIDENT; - t[*tokens]->u.fident=identcode("x",1); - t[*tokens+1]->type=OPERATOR; - t[*tokens+1]->u.op=OP; - t[*tokens+2]->type=OPERATOR; - t[*tokens+2]->u.op=CP; + t[*tokens]->type = FIDENT; + t[*tokens]->u.fident = FUNC_X; + t[*tokens+1]->type = OPERATOR; + t[*tokens+1]->u.op = OP; + t[*tokens+2]->type = OPERATOR; + t[*tokens+2]->u.op = CP; #if WK1DEBUG fprintf(se,"x()"); #endif @@ -269,12 +271,12 @@ static int pair(const char *s, Token **t, int *tokens) if (t) { if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+2]=malloc(sizeof(Token)))==(Token*)0) return -1; - t[*tokens]->type=FIDENT; - t[*tokens]->u.fident=identcode("y",1); - t[*tokens+1]->type=OPERATOR; - t[*tokens+1]->u.op=OP; - t[*tokens+2]->type=OPERATOR; - t[*tokens+2]->u.op=CP; + t[*tokens]->type = FIDENT; + t[*tokens]->u.fident = FUNC_Y; + t[*tokens+1]->type = OPERATOR; + t[*tokens+1]->u.op = OP; + t[*tokens+2]->type = OPERATOR; + t[*tokens+2]->u.op = CP; #if WK1DEBUG fprintf(se,"y()"); #endif @@ -355,10 +357,10 @@ static int sumup(const char *s, const int *offset, int top, Token **t, int *toke if (t) { if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0) return -1; - t[*tokens]->type=FIDENT; - t[*tokens]->u.fident=identcode("sum",3); - t[*tokens+1]->type=OPERATOR; - t[*tokens+1]->u.op=OP; + t[*tokens]->type = FIDENT; + t[*tokens]->u.fident = FUNC_SUM; + t[*tokens+1]->type = OPERATOR; + t[*tokens+1]->u.op = OP; #if WK1DEBUG fprintf(se,"[sum(]"); #endif @@ -411,8 +413,8 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke if (t) { if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1; - t[*tokens]->type=FIDENT; - t[*tokens]->u.fident=identcode("@",1); + t[*tokens]->type = FIDENT; + t[*tokens]->u.fident = FUNC_AT_SYMBOL; } if (tokens) ++(*tokens); if (pair(s+offset[top]+1,t,tokens)==-1) low=-1; else low=top; @@ -424,8 +426,8 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke if (t) { if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1; - t[*tokens]->type=FIDENT; - t[*tokens]->u.fident=identcode("&",1); + t[*tokens]->type = FIDENT; + t[*tokens]->u.fident = FUNC_AMPERSAND; #if WK1DEBUG fprintf(se,"[&]"); #endif @@ -435,10 +437,10 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke if (t) { if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0) return -1; - t[*tokens]->type=OPERATOR; - t[*tokens]->u.fident=COMMA; - t[*tokens+1]->type=FIDENT; - t[*tokens+1]->u.op=identcode("&",1); + t[*tokens]->type = OPERATOR; + t[*tokens]->u.fident = COMMA; + t[*tokens+1]->type = FIDENT; + t[*tokens+1]->u.op = FUNC_AMPERSAND; #if WK1DEBUG fprintf(se,"[,&]"); #endif @@ -683,6 +685,8 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke } /*}}}*/ +static char internal_errbuf[4096]; + /* loadwk1 -- load WK1 file */ /*{{{*/ const char *loadwk1(Sheet *sheet, const char *name) { @@ -847,17 +851,14 @@ const char *loadwk1(Sheet *sheet, const char *name) /* INTEGER -- Integer number cell */ /*{{{*/ case 0xD: { - Token **t; + Token t; - assert(bodylen==7); + assert(bodylen == 7); tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; cell = initcellofsheet(sheet, tmp); - t=malloc(2*sizeof(Token*)); - t[0]=malloc(sizeof(Token)); - t[1]=(Token*)0; - t[0]->type=INT; - t[0]->u.integer=it(body+5); - putcont(sheet, tmp, t, BASE); + t.type = INT; + t.u.integer = it(body+5); + puttok(sheet, tmp, t, BASE_CONT); format((unsigned char)body[0], cell); break; } @@ -865,17 +866,14 @@ const char *loadwk1(Sheet *sheet, const char *name) /* NUMBER -- Floating point number */ /*{{{*/ case 0xE: { - Token **t; + Token t; - assert(bodylen==13); + assert(bodylen == 13); tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; cell = initcellofsheet(sheet, tmp); - t=malloc(2*sizeof(Token*)); - t[0]=malloc(sizeof(Token)); - t[1]=(Token*)0; - t[0]->type=FLOAT; - t[0]->u.flt=dbl((unsigned char*)body+5); - putcont(sheet, tmp, t, BASE); + t.type = FLOAT; + t.u.flt = dbl((unsigned char*)body+5); + puttok(sheet, tmp, t, BASE_CONT); format((unsigned char)body[0], cell); break; } @@ -883,17 +881,14 @@ const char *loadwk1(Sheet *sheet, const char *name) /* _("lL)abel") -- Label cell */ /*{{{*/ case 0xF: { - Token **t; + Token t; - assert(bodylen>=6 && bodylen<=245); + assert(bodylen >= 6 && bodylen <= 245); tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; cell = initcellofsheet(sheet, tmp); - t=malloc(2*sizeof(Token*)); - t[0]=malloc(sizeof(Token)); - t[1]=(Token*)0; - t[0]->type=STRING; - t[0]->u.string=strdup(body+6); - putcont(sheet, tmp, t, BASE); + t.type = STRING; + t.u.string = strdup(body+6); + puttok(sheet, tmp, t, BASE_CONT); format((unsigned char)body[0], cell); break; } @@ -906,8 +901,12 @@ const char *loadwk1(Sheet *sheet, const char *name) int tokens; Token **t; - assert(bodylen>15); - if ((offset=malloc(it(body+13)*sizeof(int)))==0) { err=_("Out of memory"); goto ouch; } + assert(bodylen > 15); + if ((offset = malloc(it(body+13)*sizeof(int))) == 0) + { + err = _("Out of memory"); + goto ouch; + } #if WK1DEBUG fprintf(se,"FORMULA: &(%d,%d)=",it(body+1),it(body+3)); #endif @@ -971,15 +970,25 @@ const char *loadwk1(Sheet *sheet, const char *name) } else { + Token tok = eval_safe(t, LITERAL); + tvecfree(t); + if (tok.type == EEK) + { + strcpy(internal_errbuf, _("Parse error: ")); + strcat(internal_errbuf, tok.u.err); + tfree(&tok); + err = internal_errbuf; + goto ouch; + } #if WK1DEBUG - fprintf(se,"[<-- %d tokens]\r\n",tokens); + fprintf(se,"[<-- %d tokens]\r\n",tokens); #endif - free(offset); - tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; - cell = initcellofsheet(sheet, tmp); - cell->value.type=FLOAT; - cell->value.u.flt=dbl((unsigned char*)body+5); - putcont(sheet, tmp, t, BASE); + free(offset); + tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; + cell = initcellofsheet(sheet, tmp); + cell->tok[CURR_VAL].type = FLOAT; + cell->tok[CURR_VAL].u.flt = dbl((unsigned char*)body+5); + puttok(sheet, tmp, tok, BASE_CONT); } break; } @@ -1090,17 +1099,14 @@ const char *loadwk1(Sheet *sheet, const char *name) /* STRING -- Value of string formula */ /*{{{*/ case 0x33: { - Token **t; + Token t; assert(bodylen>=6 && bodylen<=245); tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; cell = initcellofsheet(sheet, tmp); - t=malloc(2*sizeof(Token*)); - t[0]=malloc(sizeof(Token)); - t[1]=(Token*)0; - t[0]->type=STRING; - t[0]->u.string=strdup(body+5); - putcont(sheet, tmp, t, BASE); + t.type = STRING; + t.u.string = strdup(body+5); + puttok(sheet, tmp, t, BASE_CONT); format((unsigned char)body[0], cell); break; } diff --git a/src/common/xdr.c b/src/common/xdr.c index 20b07e2..5e27e64 100644 --- a/src/common/xdr.c +++ b/src/common/xdr.c @@ -21,20 +21,43 @@ array terminated by a special element. #endif #include +#include #include #include #include +#include "default.h" +#include "display.h" +#include "eval.h" +#include "main.h" +#include "parser.h" #include "sheet.h" #include "xdr.h" /*}}}*/ +typedef struct Old_token_struc +{ + Type type; + union + { + char *string; + double flt; + long integer; + Operator op; + char *lident; + int fident; + int location[3]; + char *err; + } u; +} OldToken; + /* xdr_token */ /*{{{*/ -static bool_t xdr_token(XDR *xdrs, Token *t) +static bool_t xdr_token(XDR *xdrs, OldToken *t) { int x,result; - if (xdrs->x_op==XDR_DECODE) (void)memset(t,0,sizeof(Token)); + if (xdrs->x_op == XDR_DECODE) memset(t,0,sizeof(OldToken)); + else return false; x=t->type; if (t->type==OPERATOR) x|=t->u.op<<8; result=xdr_int(xdrs,&x); @@ -104,31 +127,35 @@ static bool_t xdr_token(XDR *xdrs, Token *t) return 0; } /*}}}*/ + /* xdr_tokenptr */ /*{{{*/ -static bool_t xdr_tokenptr(XDR *xdrs, Token **t) +static bool_t xdr_tokenptr(XDR *xdrs, OldToken **t) { - return xdr_pointer(xdrs,(char**)t,sizeof(Token),(xdrproc_t)xdr_token); + bool_t nonnull; + if (!xdr_bool(xdrs, &nonnull)) return false; + if (!nonnull) { *t = (OldToken *)0; return true; } + *t = malloc(sizeof(OldToken)); + return xdr_token(xdrs, *t); } /*}}}*/ + /* xdr_tokenptrvec */ /*{{{*/ -static bool_t xdr_tokenptrvec(XDR *xdrs, Token ***t) +static bool_t xdr_tokenptrvec(XDR *xdrs, OldToken ***t) { unsigned int len; int result; - assert(t!=(Token***)0); - if (xdrs->x_op!=XDR_DECODE) - { - Token **run; - - if (*t==(Token**)0) len=0; - else for (len=1,run=*t; *run!=(Token*)0; ++len,++run); - } - result=xdr_array(xdrs,(char**)t,&len,65536,sizeof(Token*),(xdrproc_t)xdr_tokenptr); - if (len==0) *t=(Token**)0; - return result; + assert(t!=(OldToken***)0); + if (xdrs->x_op!=XDR_DECODE) assert(0); + if (!xdr_u_int(xdrs, &len)) return false; + *t = malloc(sizeof(OldToken*) * (len+1)); + (*t)[len] = (OldToken*)0; + for (size_t i = 0; i < len; ++i) + if (!xdr_tokenptr(xdrs, *t + i)) return false; + return true; } /*}}}*/ + /* xdr_mystring */ /*{{{*/ static bool_t xdr_mystring(XDR *xdrs, char **str) { @@ -165,10 +192,49 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell) { int result,x; - assert(cell!=(Cell*)0); - if (!(xdr_tokenptrvec(xdrs, &(cell->contents[BASE])) - && xdr_tokenptrvec(xdrs, &(cell->contents[ITERATIVE])) /* && xdr_token(xdrs, &(cell->value)) */ )) - return 0; + assert(cell != (Cell*)0); + assert(xdrs->x_op == XDR_DECODE); + OldToken **ot; + for (TokVariety tv = BASE_CONT; tv <= ITER_CONT; ++tv) { + if (!xdr_tokenptrvec(xdrs, &ot)) return false; + size_t veclen = 0; + while (ot[veclen]) ++veclen; + Token **nt = malloc(sizeof(Token*) * (veclen + 1)); + nt[veclen] = NULLTOKEN; + for (size_t ti = 0; ti < veclen; ++ti) + { + nt[ti] = malloc(sizeof(Token)); + cleartoken(nt[ti]); + nt[ti]->type = ot[ti]->type; + switch (ot[ti]->type) { + case EMPTY: break; + case STRING: nt[ti]->u.string = ot[ti]->u.string; break; + case FLOAT: nt[ti]->u.flt = ot[ti]->u.flt; break; + case INT: nt[ti]->u.integer = ot[ti]->u.integer; break; + case OPERATOR: nt[ti]->u.op = ot[ti]->u.op; break; + case LIDENT: nt[ti]->u.lident = ot[ti]->u.lident; break; + case FIDENT: nt[ti]->u.fident = ot[ti]->u.fident; break; + case LOCATION: + nt[ti]->u.location[0] = ot[ti]->u.location[0]; + nt[ti]->u.location[1] = ot[ti]->u.location[1]; + nt[ti]->u.location[2] = ot[ti]->u.location[2]; + break; + case EEK: + nt[ti]->u.err = ot[ti]->u.err; break; + default: + assert(0); + } + free(ot[ti]); + } + free(ot); + Token tok = eval_safe(nt, LITERAL); + tvecfree(nt); + if (tok.type == EEK) { + tfree(&tok); + return false; + } + cell->tok[tv] = tok; + } if (xdr_mystring(xdrs, &(cell->label))==0) return 0; x=cell->adjust; result=xdr_int(xdrs, &x); @@ -188,12 +254,14 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell) return result; } /*}}}*/ + /* xdr_column */ /*{{{*/ bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width) { return (xdr_int(xdrs, x) && xdr_int(xdrs, z) && xdr_int(xdrs, width)); } /*}}}*/ + /* xdr_magic */ /*{{{*/ #define MAGIC0 (('#'<<24)|('!'<<16)|('t'<<8)|'e') #define MAGIC1 (('a'<<24)|('p'<<16)|('o'<<8)|'t') @@ -209,3 +277,150 @@ bool_t xdr_magic(XDR *xdrs) return (xdr_long(xdrs,&m0) && m0==MAGIC0 && xdr_long(xdrs,&m1) && m1==MAGIC1 && xdr_long(xdrs,&m2) && m2==MAGIC2); } /*}}}*/ + +/* savexdr -- save a spread sheet in XDR */ /*{{{*/ +const char *savexdr(Sheet *sheet, const char *name, unsigned int *count) +{ + return _("Saving in xdr format is no longer supported. Use portable text."); + /* variables */ /*{{{*/ + FILE *fp; + XDR xdrs; + int x,y,z; + /*}}}*/ + + *count=0; + if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno); + xdrstdio_create(&xdrs,fp,XDR_ENCODE); + if (!xdr_magic(&xdrs)) + { + xdr_destroy(&xdrs); + (void)fclose(fp); + return strerror(errno); + } + for (x=sheet->dimx-1; x>=0; --x) for (z=sheet->dimz-1; z>=0; --z) + { + int width; + int u; + + width=columnwidth(sheet,x,z); + if (width!=DEF_COLUMNWIDTH) + { + u=0; + if (xdr_int(&xdrs,&u)==0 || xdr_column(&xdrs,&x,&z,&width)==0) + { + xdr_destroy(&xdrs); + (void)fclose(fp); + return strerror(errno); + } + } + for (y=sheet->dimy-1; y>=0; --y) + { + if (CELL_ATC(sheet,x,y,z)!=NULLCELL) + { + u=1; + if (xdr_int(&xdrs,&u)==0 || xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0 || xdr_cell(&xdrs,CELL_ATC(sheet,x,y,z))==0) + { + xdr_destroy(&xdrs); + (void)fclose(fp); + return strerror(errno); + } + ++*count; + } + } + } + xdr_destroy(&xdrs); + if (fclose(fp)==EOF) return strerror(errno); + sheet->changed=0; + return (const char*)0; +} +/*}}}*/ + +/* loadxdr -- load a spread sheet in XDR */ /*{{{*/ +const char *loadxdr(Sheet *sheet, const char *name) +{ + /* variables */ /*{{{*/ + FILE *fp; + XDR xdrs; + Location w; + int width; + int u; + int olderror; + Cell *nc; + /*}}}*/ + + if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno); + xdrstdio_create(&xdrs,fp,XDR_DECODE); + if (!xdr_magic(&xdrs)) + { +#if 0 + xdr_destroy(&xdrs); + fclose(fp); + return _("This is not a teapot worksheet in XDR format"); +#else + xdr_destroy(&xdrs); + rewind(fp); + xdrstdio_create(&xdrs,fp,XDR_DECODE); +#endif + } + freesheet(sheet,0); + while (xdr_int(&xdrs,&u)) switch (u) + { + /* 0 -- column width element */ /*{{{*/ + case 0: + { + if (xdr_column(&xdrs,&(w[X]),&(w[Z]),&width)==0) + { + olderror=errno; + xdr_destroy(&xdrs); + (void)fclose(fp); + return strerror(olderror); + } + setwidth(sheet,w[X],w[Z],width); + break; + } + /*}}}*/ + /* 1 -- cell element */ /*{{{*/ + case 1: + { + if (xdr_int(&xdrs,&(w[X]))==0 || + xdr_int(&xdrs,&(w[Y]))==0 || + xdr_int(&xdrs,&(w[Z]))==0) + { + olderror=errno; + xdr_destroy(&xdrs); + (void)fclose(fp); + return strerror(olderror); + } + nc = initcellofsheet(sheet, w); + if (xdr_cell(&xdrs, nc)==0) + { + freecellofsheet(sheet, w); + olderror=errno; + xdr_destroy(&xdrs); + (void)fclose(fp); + return strerror(olderror); + } + break; + } + /*}}}*/ + /* default -- should not happen */ /*{{{*/ + default: + { + xdr_destroy(&xdrs); + fclose(fp); + sheet->changed=0; + cachelabels(sheet); + forceupdate(sheet); + return _("Invalid record, loading aborted"); + } + /*}}}*/ + } + xdr_destroy(&xdrs); + if (fclose(fp)==EOF) return strerror(errno); + sheet->changed=0; + cachelabels(sheet); + forceupdate(sheet); + redraw_sheet(sheet); + return (const char*)0; +} +/*}}}*/ diff --git a/src/common/xdr.h b/src/common/xdr.h index 403ff4c..44a9ce3 100644 --- a/src/common/xdr.h +++ b/src/common/xdr.h @@ -17,4 +17,7 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell); bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width); bool_t xdr_magic(XDR *xdrs); +const char *savexdr(Sheet *sheet, const char *name, unsigned int *count); +const char *loadxdr(Sheet *sheet, const char *name); + #endif diff --git a/src/display.c b/src/display.c index 0ae3a09..02ec5e7 100644 --- a/src/display.c +++ b/src/display.c @@ -565,13 +565,15 @@ void redraw_sheet(Sheet *sheet) else { cell = curcell(sheet); - print(buf+strlen(buf), bufsz-strlen(buf), 0, 1, getscientific(cell), - -1, getcont(cell, BASE)); - if (getcont(cell, ITERATIVE) && mbslen(buf) < (size_t)(sheet->maxx+1-4)) + Token bc = gettok(cell, BASE_CONT); + printtok(buf+strlen(buf), bufsz-strlen(buf), 0, 1, getscientific(cell), + -1, false, &bc); + Token ic = gettok(cell, ITER_CONT); + if (ic.type != EMPTY && mbslen(buf) < (size_t)(sheet->maxx+1-4)) { strcat(buf," -> "); - print(buf+strlen(buf), bufsz-strlen(buf), 0, 1, - getscientific(cell), -1, getcont(cell, ITERATIVE)); + printtok(buf+strlen(buf), bufsz-strlen(buf), 0, 1, + getscientific(cell), -1, false, &ic); } } *mbspos(buf, sheet->maxx) = 0; diff --git a/src/fteapot.fl b/src/fteapot.fl index f5a245a..59240b2 100644 --- a/src/fteapot.fl +++ b/src/fteapot.fl @@ -167,10 +167,11 @@ class TeapotTable {open : {public Fl_Table}} fl_measure(s, ww, hh, 0); if (ww > W-6) for (int i = 0; s[i]; i++) s[i] = '\#'; - int adj = getadjust(cell); + Adjust adj = getadjust(cell); fl_draw(s, xx+3, yy+3, W-6, H-6, adj == RIGHT ? FL_ALIGN_RIGHT : - adj == LEFT ? FL_ALIGN_LEFT : FL_ALIGN_CENTER); + adj == LEFT ? FL_ALIGN_LEFT : FL_ALIGN_CENTER, + NULL, 0); if (underlined(cell)) fl_xyline(xx, yy+H-7, xx+W); fl_pop_clip(); @@ -660,14 +661,15 @@ class MainWindow {open} free(err); val[sizeof(val)-1] = 0; } else { - print(val, sizeof(val), 0, 1, getscientific(curcell(sheet)), - -1, getcont(curcell(sheet), BASE)); - Token **iter = getcont(curcell(sheet), ITERATIVE); - if (iter != EMPTY_TVEC) { + Token t = gettok(curcell(sheet), BASE_CONT); + printtok(val, sizeof(val), 0, 1, getscientific(curcell(sheet)), + -1, true, &t); + t = gettok(curcell(sheet), ITER_CONT); + if (t.type != EMPTY) { snprintf(val+strlen(val),sizeof(val)-strlen(val), " -> "); - print(val+strlen(val), sizeof(val)-strlen(val), 0, 1, - getscientific(curcell(sheet)), -1, iter); + printtok(val+strlen(val), sizeof(val)-strlen(val), 0, 1, + getscientific(curcell(sheet)), -1, true, &t); } }