feat: Type-dependent Haskell code generation
continuous-integration/drone/push Build is failing Details

Caveat: type is still not being assigned for the Sequence()
  constructor.

  Also fixes the parsing of literal strings (whitespace just after
  the opening quote was being ignored, and was ambiguous just
  before the opening quote).
This commit is contained in:
Glen Whitney 2021-02-16 09:46:12 -08:00
parent 5cd75b8177
commit 804a00902a
6 changed files with 62 additions and 33 deletions

View File

@ -11,7 +11,8 @@ language as possible to work in, given that I inevitably will be doing a
bunch of coding. The language will be centrally organized around the
concept of "streams" (somewhat in the spirit of
[streem](https://github.com/matz/streem) and/or
[Orc](http://orc.csres.utexas.edu/index.shtml)). In fact all higher-type
[Orc](http://orc.csres.utexas.edu/index.shtml), or to a lesser extent,
[Sisal-is](https://github.com/parsifal-47/sisal-is)). In fact all higher-type
entities will be cast in terms of streams, or in slogan form, "++f++unctions
and (binary) ++o++perators are ++str++eams" (hence the name "fostr").

View File

@ -14,7 +14,7 @@ lexical sorts
lexical syntax
STRING_LITERAL = ~[\']*
STRING_LITERAL = "'"~[\']*"'"
context-free sorts
@ -38,7 +38,7 @@ context-free syntax
TermEx.Terminate = <<Ex>;>
Ex.Int = INT
Ex.LitString = <'<STRING_LITERAL>'>
Ex.LitString = STRING_LITERAL
Ex.Stream = <stream>
Ex.Sum = <<Ex> + <Ex>> {left}
Ex.Gets = [[Ex] << [Ex]] {left}

View File

@ -1,15 +1,15 @@
module basic
language fostr
test hw1_type [[
[[stream]] << [['Hello, world!']]
[[stream]] << [['Hello, world! ']] << [[3+2]] << ' times.'
]]
run get-type on #1 to STREAM()
run get-type on #2 to STRING()
run get-type on #3 to INT()
run get-type to STREAM()
/** writes
Hello, world!**/
Hello, world! 5 times.**/
/** md
Title: A whirlwind tour of fostr

View File

@ -1,15 +1,25 @@
module haskell
imports libstrategolib signatures/- util
imports libstrategolib signatures/- util analysis
rules
/* Approach: Generate code from the bottom up.
At every node, we create a pair of the implementation and
necessary preamble of IO actions.
We concatenate preambles as we go up.
Finally, at the toplevel we emit the preamble before returning the
final value.
/* Approach:
A) We will define a local transformation taking a term with value strings
at each child to a value string for the node.
B) We will append IO actions needed to set up for the value progressively
to a Preactions rule (mapping () to the list of actions). There will
be a utility `add-preaction` to append a new clause to value of this
rule.
C) We will use bottomup-para to traverse the full AST with the
transformation from A so that we have access to the original expression
(and get get the Statix-associated type when we need to).
Hence the transformation in (A) must actually take a pair of
an (original) term and a term with value strings at each child,
and be certain to return a value string.
Finally, at the toplevel we emit the result of <Preactions>() before
returning the final value.
*/
hs: TopLevel((c,p)) -> $[import System.IO
hs: (_, TopLevel(val)) -> $[import System.IO
data IOStream = StdIO
gets :: Show b => a -> b -> IO a
@ -17,25 +27,35 @@ rules
putStr(show d)
return s
getsStr :: a -> String -> IO a
getsStr s d = do
putStr(d)
return s
main = do
[p]return [c]]
[<Preactions>()]return [val]]
hs: Stream() -> ("StdIO", "")
hs: Int(x) -> (x, "")
hs: LitString(x)
-> ($["[<string-as-chars(escape-chars(HaskellEscape))>x]"], "")
hs: Sum( (c, p), (d, q)) -> ($[([c] + [d])], <conc-strings>(p,q))
hs: (_, Stream()) -> "StdIO"
hs: (_, Int(x)) -> x
hs: (_, LitString(x)) -> <haskLitString>x
hs: (_, Sum(x, y)) -> $[([x] + [y])]
hs: Gets((c, p), (d, q)) -> <hsget>(c,d,<conc-strings>(p,q),<newname>"fosgt")
hsget: (s, x, p, v) -> (v, <concat-strings>[p, $[[v] <- [s] `gets` [x]],
"\n"])
hs: (Gets(_, xn), Gets(s, x)) -> v
with v := <newname>"_fostr_get"
; <add-preactions>[$[[v] <- [<hs_gets>(s, xn, x)]]]
hs: (To(xn, _), To(x, s)) -> v
with v := <newname>"_fostr_to"
; <add-preactions>[$[let [v] = [x]], <hs_gets>(s, xn, v)]
hs: To( (c, p), (d, q)) -> <hsto>(c,d,<conc-strings>(p,q),<newname>"fosto")
hsto: (x, s, p, v) -> (v, <concat-strings>[p, $[let [v] = [x]], "\n",
$[[s] `gets` [v]], "\n"])
hs_gets: (s, xn, x ) -> $[[s] [<hs_getOp>xn] [x]]
hs_getOp = get-type; (?STRING() < !"`getsStr`" + !"`gets`")
hs: Terminate((c,p)) -> ($[[c];;], p)
hs: Sequence(l) -> (<last; Fst>l, <map(Snd); concat-strings>l)
hs: (_, Terminate(x)) -> $[[x];;]
hs: (_, Sequence(l)) -> <last>l
/* One drawback of using paramorphism is at the very leaves we have
to undouble the tuple:
*/
hs: (x, x) -> x where <is-string>x
/* Characters we need to escape in Haskell string constants */
Hascape: ['\t' | cs ] -> ['\', 't' | cs ]
@ -47,9 +67,15 @@ rules
Hascape: [ 12 | cs ] -> ['\', 'f' | cs ] // Form feed
strategies
HaskellEscape = Escape <+ Hascape
haskLitString = un-single-quote
; string-as-chars(escape-chars(Escape <+ Hascape))
; double-quote
haskell = bottomup(try(hs))
haskell = rules(Preactions: () -> ""); bottomup-para(try(hs))
/* See "Approach" at top of file */
add-preactions = newp := <conc-strings>(<Preactions>(), <lines>)
; rules(Preactions: () -> newp)
// Interface haskell code generation with editor services and file system
to-haskell: (selected, _, _, path, project-path) -> (filename, result)

View File

@ -13,7 +13,7 @@ rules
js: Stream() -> $[Stdio]
js: Int(x) -> x
js: LitString(x) -> $['[<string-as-chars(escape-chars(JavaEscape))>x]']
js: LitString(x) -> <javaLitString>x
js: Sum(x,y) -> $[[x] + [y]]
js: Gets(x, y) -> $[[x].gets([y])]
js: To(x, y) -> $[to([x],[y])]
@ -29,7 +29,9 @@ rules
Jscape: [ 12 | cs ] -> ['\', 'f' | cs ] // Form feed
strategies
JavaEscape = Escape <+ Jscape
javaLitString = un-single-quote
; string-as-chars(escape-chars(Escape <+ Jscape))
; single-quote
javascript = bottomup(try(js))

View File

@ -15,7 +15,7 @@ rules
py: Stream() -> $[Stdio]
py: Int(x) -> x
py: LitString(x) -> $[r'[x]']
py: LitString(x) -> $[r[x]]
py: Sum(x,y) -> $[[x] + [y]]
py: Gets(x, y) -> $[[x].gets([y])]
py: To(x, y) -> $[to([x],[y])]