I added the follow statements to 'form2String1'
op = "::" =>
null argl => '"::"
lo := form2String1 first argl
argl := rest argl
[lo, '"::", form2String1 first argl]
to handle unparse of the '::' operator.
http://axiom-wiki.newsynthesis.org/SandBoxUnparse#bottom
Please let me know if you have any comments or if you know a better way ...
Regards,
Bill Page.
AFAICS we need "::" (coercion), "$" (package call) and "@" (required
result type). In general we need parentheses around arguments,
in particular both "::" and "@" bind pretty tightly, but in
practice we are likely to apply them to large expressions.
Also, we do not handle logical operators ('not' is correctly
handled by application rule, but 'and' and 'or' are mangled).
Next, "rem" and "quo", comparisons and "SEQ" are just unparsed
as function applications and interpreter parser can handle them in
that form. I am not sure if we really want parser to allow
such prefix forms. If we use such prefix forms we should be
careful to avoid accidentally turning them into infix operators
--> we need a lot of parenthesis
Coming back to your code: to get corrent output you probably
want something like:
argl is [arg1, arg2] and (op = "::" or op = "@" or op = "and" ...) =>
concat('"(", '"(", form2String1 arg1, '")", form2String1 op,_
'"(", form2String1 arg2, '")", '")")
If you want to omit unneeded parentheses you need some extra logic.
Basically either one has to mimic work of parser and comupute
priorities at each node of the form, or one should use a family
of mutually recursive functions (one for each priority class).
I am writing "priority class" because at least in some cases
priorities are _not_ ordered linearly.
BTW. Package calls need a bit more of logic because in source
we have something like "sin(x)$(Expression Integer)" while the
InputForm is:
(($elt (Expression Integer) sin) x)
So, we need code like
op is ["$elt" t op1] and isType(t) =>
concat(form2String1 [op1, :argl], '"$", '"(", form2String1 t, '")")
--
Waldek Hebisch
heb...@math.uni.wroc.pl
This sounds very ambitious to me. In order to keep this project to a
reasonable size, I would like to propose we first solve a somewhat
smaller problem: I would just like to be able to properly unparse only
those values of InputForm which we can generate by coercions, i.e. in
the sense that:
parse(unparse(x::InputForm)) = x::InputForm
where x is any value for which such a coercion already exists. I would
like to largely ignore for now the other more direct ways by which one
can construct values of InputForm. For example:
(1) -> (1$SquareMatrix(3,Polynomial Complex Integer))::InputForm
(1)
(squareMatrix
(matrix
(construct (construct (complex 1 0) (complex 0 0) (complex 0 0))
(construct (complex 0 0) (complex 1 0) (complex 0 0))
(construct (complex 0 0) (complex 0 0) (complex 1 0)))
)
)
Type: InputForm
(2) -> test(parse(unparse(%))$InputForm=%)
(2) true
Type: Boolean
I think this is a much smaller set than all possible values of
InputForm since typically such coercions attempt to find some simple
and direct construction that evaluate to a given value. The most
complicated of these originate in "symbolic" domains like Polynomial
and Expression. Of course as we add new domains and new functionality
to old domains we may end up expanding the set of InputForms that we
can generate. When/if we do this, I think that is also a good time to
consider what extensions (if any) might be necessary in order to
unparse these values.
InputForm values generated by coercions are exactly those that we need
to be able to unparse for the Sage interface (and perhaps as input to
other systems) so that with a minimum translation they may often be
able to reconstruct accurate versions of these values.
So far I do not know of any way to generate $ package calls or @
result assertions through such coercions.
> ...
Regards,
Bill Page.
Currently large part of coercion to InputForm is uniplemented and
some parts are wrong (like the example you showed). I believe that
package calls and @ are important tools when fixing this problem.
Below is improved patch: I have cleaned code a bit and added support
for "::", "@", "$" (package calls) and "pretend". I keep handling
of "/" and unary minus like in original patch.
BTW: Yesterday I checked (with earlier version) that most of differences
in output is due to parentheses around single argument to function
calls. While this may be treated as regression, I think that
such parentheses save us a lot of trouble so I prefer to always
have them. Other changes seem to be smaller and looked OK.
diff -ru dist.bb/src/interp/format.boot dist/src/interp/format.boot
--- dist.bb/src/interp/format.boot 2008-09-17 00:09:51.000000000 +0000
+++ dist/src/interp/format.boot 2008-09-18 15:13:08.000000000 +0000
@@ -433,14 +433,78 @@
-- fortranCleanUp exp2Fort1 [op,:argl]
-- somewhat works, but causes regression
-- fortranCleanUp exp2Fort1 exp2FortOptimize [op,:argl]
- isBinaryInfix op => binop2String [op,:argl]
+ u1 is ["$elt", t, f] =>
+ concat(form2String1 f, '"$", form2String1 t)
+ #argl = 2 and (isBinaryInfix op or op = "::" or op = '"::"_
+ or op = "@" or op = '"@" or op = "pretend" or op = '"pretend") =>
+ binop2String [op,:argl]
application2String(op,[form2String1 x for x in argl], u1)
binop2String x ==
- $exp2FortTempVarIndex : local := 0
- $fortName : fluid := newFortranTempVar()
- $fortInts2Floats : fluid := nil
- fortranCleanUp exp2Fort1 exp2FortOptimize x
+ $curOp : local := CAR x
+ x is ["=", arg1, arg2] or x is ['"=", arg1, arg2] =>
+ concat(sumOrParen(arg1), '"=", sumOrParen(arg2))
+ sumOrParen(x)
+ sumOrParen(x)
+
+sumOrParen(x) ==
+ x is [op, arg1, arg2] =>
+ op = "+" or op = '"+" =>
+ concat(sumOrParen(arg1), '"+", productOrParen(arg2))
+ op = "-" or op = '"-" =>
+ concat(sumOrParen(arg1), '"-", productOrParen(arg2))
+ op = "/" or op = '"/" =>
+ concat(appOrParen(arg1), '"/", appOrParen(arg2))
+ productOrParen(x)
+ productOrParen(x)
+
+productOrParen(x) ==
+ x is [op, arg1, arg2] =>
+ op = "*" or op ='"*" =>
+ concat(productOrParen(arg1), '"*", powerOrParen(arg2))
+ powerOrParen(x)
+ powerOrParen(x)
+
+powerOrParen(x) ==
+ x is [op, arg1, arg2] =>
+ op = "**" or op = '"**" or op = "^" or op = '"^" =>
+ concat(coerceOrParen(arg1), '"^", coerceOrParen(arg2))
+ coerceOrParen(x)
+ coerceOrParen(x)
+
+coerceOrParen(x) ==
+ x is [op, arg1, arg2] =>
+ op = "::" or op = '"::" =>
+ concat(coerceOrParen(arg1), '"::", appOrParen(arg2))
+ op = "@" or op = '"@" =>
+ concat(coerceOrParen(arg1), '"@", appOrParen(arg2))
+ op = "pretend" or op = '"pretend" =>
+ concat(coerceOrParen(arg1), '" ", '"pretend", '" ",_
+ appOrParen(arg2))
+ appOrParen(x)
+ appOrParen(x)
+
+appOrParen(x) ==
+ SYMBOLP(x) => formWrapId x
+ INTEGERP(x) => WRITE_-TO_-STRING x
+ ATOM(x) => concat('"(", form2String1(x), '")")
+ [op, :argl] := x
+ (op = "-" or op = '"-") and #argl = 1 =>
+ concat('"(", '"-", appOrParen(first argl), '")")
+ op = $curOp => BREAK()
+ op is ["$elt", f, t] =>
+ form2String1 x
+ -- Put parenthesis around anything special
+ not(SYMBOLP op) or GET(op, "Led") or GET(op, "Nud")_
+ or op= 'mkCategory or op = "SEGMENT" _
+ or op = 'construct or op = 'COLLECT or op = "SIGNATURE"_
+ or op = 'BRACKET or op = 'AGGLST or op = "ATTRIBUTE"_
+ or op = "#" =>
+ concat('"(", form2String1(x), '")")
+ op = "Zero" => '"0"
+ op = "One" => '"1"
+ form2String1 x
+
formWrapId id ==
$formatSigAsTeX = 1 => id
@@ -622,12 +686,15 @@
SUBSTRING(op',s,e-s)
application2String(op,argl, linkInfo) ==
+ op is ["$elt", t, f] =>
+ concat(application2String(f, argl, linkInfo), '"$", _
+ form2String1 t)
null argl =>
(op' := isInternalFunctionName(op)) => op'
app2StringWrap(formWrapId op, linkInfo)
1=#argl =>
first argl is ["<",:.] => concat(op,first argl)
- concat(app2StringWrap(formWrapId op, linkInfo)," ",first argl)
+ concat(app2StringWrap(formWrapId op, linkInfo), '"(", first argl, '")")
--op in '(UP SM) =>
-- newop:= (op = "UP" => "P";"M")
-- concat(newop,concat(lbrkSch(),argl.0,rbrkSch(),argl.1))
--
Waldek Hebisch
heb...@math.uni.wroc.pl
I understand the issue but I am not yet convinced that package calls
and result assertions are necessary or desirable when coercing other
values to InputForm. On the other hand, I have no objection of course
to including these for other reasons.
In the case of coercion to InputForm most domains naturally implement
some simple ways to generate values of this domain. Usually these are
chosen to be convenient for the user or for the interpreter interface
to the library. For example, the 'Float' domain exports the function
'float'
(1) -> float(1,0,2)
(1) 1.0
Type: Float
So it is very natural that we implement coercion from Float to InputForm as
(2) -> unparse(%::InputForm)
(2) "float(1,0,2)"
Type: String
Although we could generate "float(1,0,2)$Float" in this case we do not
need to generate the package call because the signature of
'float$Float' is deliberately chosen to be unambiguous in the
interpreter (parse). I believe this is typical of the design of most
domains.
> Below is improved patch: I have cleaned code a bit and added
> support for "::", "@", "$" (package calls) and "pretend". I keep
> handling of "/" and unary minus like in original patch.
>
I just tried this patch to 'format.boot' from FriCAS rev: 370 using
the examples on the wiki page:
http://axiom-wiki.newsynthesis.org/SandBoxUnparse
This version fails on the following example that we previously discussed:
integrate(sin(x)^2+cos(x+1)^2,x)
unparse(%::InputForm)
It results in the error message:
Break.
Broken at FUNCALL. Type :H for Help.
BOOT>>
>> System error:
The variable READ is unbound.
and the following two tests involving repeated division
parse("a*b/c/d")
unparse(%)
parse("a/b/c/d")
unparse(%)
result in the same message.
All other results on this page are the same as shown on this page
except for few more redundant parenthesis in some cases (due to
different treatment of / ).
> BTW: Yesterday I checked (with earlier version) that most of
> differences in output is due to parentheses around single
> argument to function calls. While this may be treated as
> regression, I think that such parentheses save us a lot of trouble
> so I prefer to always have them.
I agree except perhaps for relative precedence of * and /. But this
redundancy does not cause any problem.
> Other changes seem to be smaller and looked OK.
>
Great. It would be nice to have this new version of 'format.boot' in
the next release of FriCAS.
> diff -ru dist.bb/src/interp/format.boot dist/src/interp/format.boot
> --- dist.bb/src/interp/format.boot 2008-09-17 00:09:51.000000000 +0000
> +++ dist/src/interp/format.boot 2008-09-18 15:13:08.000000000 +0000
> ...
Regards,
Bill Page.
What you wrote works for "simple" domains. But many domains have
parameters. For example, given:
(x^3+1)::Polynomial PF(5)
you need some way to get the prime 5 into InputForm.
> > Below is improved patch: I have cleaned code a bit and added
> > support for "::", "@", "$" (package calls) and "pretend". I keep
> > handling of "/" and unary minus like in original patch.
> >
>
> I just tried this patch to 'format.boot' from FriCAS rev: 370 using
> the examples on the wiki page:
>
> http://axiom-wiki.newsynthesis.org/SandBoxUnparse
>
> This version fails on the following example that we previously discussed:
>
> integrate(sin(x)^2+cos(x+1)^2,x)
> unparse(%::InputForm)
>
> It results in the error message:
>
> Break.
> Broken at FUNCALL. Type :H for Help.
> BOOT>>
> >> System error:
> The variable READ is unbound.
>
Again test for infinite recursion fired when it should not.
You may remove line with BREAK from appOrParen, or better
change begining of binop2String and appOrParen to be as
below (only the 2 lines which contained '$curOp' has
changed).
binop2String x ==
$curExpr : local := x
x is ["=", arg1, arg2] or x is ['"=", arg1, arg2] =>
concat(sumOrParen(arg1), '"=", sumOrParen(arg2))
...
appOrParen(x) ==
SYMBOLP(x) => formWrapId x
INTEGERP(x) => WRITE_-TO_-STRING x
ATOM(x) => concat('"(", form2String1(x), '")")
[op, :argl] := x
(op = "-" or op = '"-") and #argl = 1 =>
concat('"(", '"-", appOrParen(first argl), '")")
EQ(x, $curExpr) => BREAK()
op is ["$elt", f, t] =>
--
Waldek Hebisch
heb...@math.uni.wroc.pl
Even in this case there is a way to express this without package calls
by just coercing the generators:
(1) -> convert([_+::InputForm, _
convert([_*_*::InputForm, _
convert(["::"::Symbol::InputForm, _
convert(["::"::Symbol::InputForm,x::InputForm,"Symbol"::Symbol::InputForm]),
_
convert(["Polynomial"::Symbol::InputForm, _
convert(["PrimeField"::Symbol::InputForm,5::InputForm]) _
]) _
]),3::InputForm _
]), _
1::InputForm _
])
(1) (+ (** (:: (:: x Symbol) (Polynomial (PrimeField 5))) 3) 1)
Type: InputForm
(2) -> interpret(%)
3
(2) x + 1
Type: Polynomial(PrimeField(5))
But you might be right in this case that a package call is more "natural".
Right now we have a different problem:
(3) -> (x^3+1)::Polynomial PF(5)
3
(3) x + 1
Type: Polynomial(PrimeField(5))
(4) -> %::InputForm
Cannot convert from type Polynomial(PrimeField(5)) to InputForm for
value
3
x + 1
This sort of coercion is not implemented yet.
> ...
> Again test for infinite recursion fired when it should not.
> You may remove line with BREAK from appOrParen, or better
> change begining of binop2String and appOrParen to be as
> below (only the 2 lines which contained '$curOp' has
> changed).
> ...
Great! This works now.
Regards,
Bill Page.
Here is a simple patch to 'interp/i-coerce.boot' that seems to provide
automatic coercion of all domains and categories into InputForm in the
interpreter. According to documentation in 'i-coerce.boot' it is also
possible to call this from algebra code but I haven't tried that yet.
The patch is based on a lot of study of how something similar was done
for coercions of domains and categories to OutputForm.
There are examples on wiki page:
http://axiom-wiki.newsynthesis.org/SubDomain
which also includes definitions of the new algebra domains 'Domain'
and 'SubDomain' for other purposes.
Of course more testing would be very desirable!
page@sage:~/fricas-src$ svn diff
Index: src/interp/i-coerce.boot
===================================================================
--- src/interp/i-coerce.boot (revision 370)
+++ src/interp/i-coerce.boot (working copy)
@@ -775,8 +775,10 @@
t2 = '$NoValueMode => objNew(val,t2)
if t2 is ['SubDomain,x,.] then t2:= x
-- JHD added category Aug 1996 for BasicMath
+ -- WSP modified for coercion to InputForm Sept 2008
t1 in '((Category) (Mode) (Domain) (SubDomain (Domain))) =>
t2 = $OutputForm => objNew(val,t2)
+ t2 = '(InputForm) => objNewWrap(val,t2)
NIL
t1 = '$NoValueMode =>
if $compilingMap then clearDependentMaps($mapName,nil)
---
Regards,
Bill Page.
Commants:
1) The last example (CliffordAlgebra...) is clearly wrong. AFAICS
we need to recursively walk the s-expression describing type and
convert all non-type parameters of types to InputForm.
2) We would like to have this functionality available at Spad level
probably in InputFormFunctions1. We probably should have a
Boot function called domainToInputForm and call it both from
algebra and from interpreter.
3) The patch could probably go in, with understanding that this
is only partial implementation.
--
Waldek Hebisch
heb...@math.uni.wroc.pl
Below is a variant of this patch. The new functions typeToInputForm
and typeToOutputForm are inteded to be called both form algebra
and from intepreter -- they are needed for types having non-type
parameters, like the CliffordAlgebra example. AFAICS chage to to the
OutputForm handling solves issue 16.
diff -u dist-358.bb/src/interp/i-coerce.boot dist-358/src/interp/i-coerce.boot
--- dist-358.bb/src/interp/i-coerce.boot 2008-09-07 19:05:39.000000000 +0200
+++ dist-358/src/interp/i-coerce.boot 2008-09-24 05:24:18.631490336 +0200
@@ -765,6 +765,23 @@
--% Interpreter Coercion Functions
+typeToInputForm(t) == typeToForm(t, '(InputForm))
+
+typeToOutputForm(t) == typeToForm(t, $OutputForm)
+
+typeToForm(t, toForm) ==
+ t0 := devaluate(t)
+ [op,:argl] := t0
+ coSig := rest GETDATABASE(op, 'COSIG)
+ sig := getConstructorSignature t0
+ ml := replaceSharps(rest sig, t0)
+ nl := [fn(x, t1, c, toForm) for x in argl for t1 in ml_
+ for c in coSig] where
+ fn(x, t1, c, toForm) ==
+ c => typeToForm(x, toForm)
+ algCoerceInteractive(x, t1, toForm)
+ [op, :nl]
+
coerceInteractive(triple,t2) ==
-- bind flag for recording/reporting instantiations
-- (see recordInstantiation)
@@ -776,7 +793,8 @@
if t2 is ['SubDomain,x,.] then t2:= x
-- JHD added category Aug 1996 for BasicMath
t1 in '((Category) (Mode) (Domain) (SubDomain (Domain))) =>
- t2 = $OutputForm => objNew(val,t2)
+ t2 = $OutputForm => objNewWrap(typeToOutputForm(val), t2)
+ t2 = '(InputForm) => objNewWrap(typeToInputForm(val), t2)
NIL
t1 = '$NoValueMode =>
if $compilingMap then clearDependentMaps($mapName,nil)
--
Waldek Hebisch
heb...@math.uni.wroc.pl
> BTW: Yesterday I checked (with earlier version) that most of differences in
> output is due to parentheses around single argument to function calls. While
> this may be treated as regression, I think that such parentheses save us a
> lot of trouble so I prefer to always have them. Other changes seem to be
> smaller and looked OK.
Although I think from the theoretical point of view, keeping the parenthesis
makes sense, I must admit that I dislike them a lot in output. Maybe it would
be possible to omit them there.
Concerning the testsuite: this is again a case for using the unittest package.
Checking the tests is very error-prone and time consuming currently, at least
for me...
Martin
There are two issues:
1) Unparsed output may be used by other programs, in such case user
probably wants parenthesis. So I think that we want _possibility_
to print parenthesis (this may be turned on or off by a flag).
2) Effort is needed to determine when we can omit parenthesis without
changing parse -- you noticed "sin(x+1)", but we also have
"sin(x)$Float" which is quite different than "sin x$Float".
> Concerning the testsuite: this is again a case for using the unittest package.
Well, the change is to _printed_ output -- currently unittest package can
not handle this...
--
Waldek Hebisch
heb...@math.uni.wroc.pl
> Well, the change is to _printed_ output -- currently unittest package can
> not handle this...
Yes, but actually we do not want to test printed output in, say,
mapleok.input...
Martin