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 bunch of coding. The language will be centrally organized around the
concept of "streams" (somewhat in the spirit of concept of "streams" (somewhat in the spirit of
[streem](https://github.com/matz/streem) and/or [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 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"). and (binary) ++o++perators are ++str++eams" (hence the name "fostr").

View File

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

View File

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

View File

@ -1,15 +1,25 @@
module haskell module haskell
imports libstrategolib signatures/- util imports libstrategolib signatures/- util analysis
rules rules
/* Approach: Generate code from the bottom up. /* Approach:
At every node, we create a pair of the implementation and A) We will define a local transformation taking a term with value strings
necessary preamble of IO actions. at each child to a value string for the node.
We concatenate preambles as we go up. B) We will append IO actions needed to set up for the value progressively
Finally, at the toplevel we emit the preamble before returning the to a Preactions rule (mapping () to the list of actions). There will
final value. 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 data IOStream = StdIO
gets :: Show b => a -> b -> IO a gets :: Show b => a -> b -> IO a
@ -17,25 +27,35 @@ rules
putStr(show d) putStr(show d)
return s return s
getsStr :: a -> String -> IO a
getsStr s d = do
putStr(d)
return s
main = do main = do
[p]return [c]] [<Preactions>()]return [val]]
hs: Stream() -> ("StdIO", "") hs: (_, Stream()) -> "StdIO"
hs: Int(x) -> (x, "") hs: (_, Int(x)) -> x
hs: LitString(x) hs: (_, LitString(x)) -> <haskLitString>x
-> ($["[<string-as-chars(escape-chars(HaskellEscape))>x]"], "") hs: (_, Sum(x, y)) -> $[([x] + [y])]
hs: Sum( (c, p), (d, q)) -> ($[([c] + [d])], <conc-strings>(p,q))
hs: Gets((c, p), (d, q)) -> <hsget>(c,d,<conc-strings>(p,q),<newname>"fosgt") hs: (Gets(_, xn), Gets(s, x)) -> v
hsget: (s, x, p, v) -> (v, <concat-strings>[p, $[[v] <- [s] `gets` [x]], with v := <newname>"_fostr_get"
"\n"]) ; <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") hs_gets: (s, xn, x ) -> $[[s] [<hs_getOp>xn] [x]]
hsto: (x, s, p, v) -> (v, <concat-strings>[p, $[let [v] = [x]], "\n", hs_getOp = get-type; (?STRING() < !"`getsStr`" + !"`gets`")
$[[s] `gets` [v]], "\n"])
hs: Terminate((c,p)) -> ($[[c];;], p) hs: (_, Terminate(x)) -> $[[x];;]
hs: Sequence(l) -> (<last; Fst>l, <map(Snd); concat-strings>l) 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 */ /* Characters we need to escape in Haskell string constants */
Hascape: ['\t' | cs ] -> ['\', 't' | cs ] Hascape: ['\t' | cs ] -> ['\', 't' | cs ]
@ -47,9 +67,15 @@ rules
Hascape: [ 12 | cs ] -> ['\', 'f' | cs ] // Form feed Hascape: [ 12 | cs ] -> ['\', 'f' | cs ] // Form feed
strategies 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 // Interface haskell code generation with editor services and file system
to-haskell: (selected, _, _, path, project-path) -> (filename, result) to-haskell: (selected, _, _, path, project-path) -> (filename, result)

View File

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

View File

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