forked from glen/fostr
Merge pull request 'feat: Allow expressions to be terminated/sequenced by ;' (#15) from semi_separate into main
Resolves #4. Reviewed-on: glen/fostr#15
This commit is contained in:
commit
02cf762ac7
@ -23,8 +23,10 @@ for path in TEST_LIST:
|
||||
tests = re.split(r'test\s*(.+?)\s*\[\[.*?\n', contents)[1:]
|
||||
testit = iter(tests)
|
||||
for name, details in zip(testit, testit):
|
||||
pfm = re.search(r'\n\s*\]\].*?parse\s*fails', details)
|
||||
if pfm: continue # skip examples that don't parse
|
||||
pfm = re.search(r'\n\s*\]\][\s\S]*?parse\s*fails', details)
|
||||
if pfm: continue # skip examples that don't parse
|
||||
ntfm = re.search(r'\n\s*\]\].*?don.t.test', details)
|
||||
if ntfm: continue # explicit skip
|
||||
em = re.search(r'\n\s*\]\]', details)
|
||||
if not em: continue
|
||||
example = details[:em.start()+1]
|
||||
|
@ -20,6 +20,7 @@ menus
|
||||
|
||||
action: "Format" = editor-format (source)
|
||||
action: "Show parsed AST" = debug-show-aterm (source)
|
||||
action: "Desugar AST" = debug-desugar-fostr (source)
|
||||
|
||||
views
|
||||
|
||||
|
@ -10,16 +10,24 @@ context-free start-symbols
|
||||
|
||||
context-free sorts
|
||||
|
||||
Start LineSeq Line Ex
|
||||
Start LineSeq Line OptTermEx TermExLst TermEx Ex
|
||||
|
||||
context-free syntax
|
||||
|
||||
Start.TopLevel = LineSeq
|
||||
|
||||
LineSeq = <<ln:Ex>> {layout(offside ln)}
|
||||
LineSeq.Sequence = sq:Ex+ {layout(align-list sq)}
|
||||
LineSeq = Line
|
||||
LineSeq.Sequence = sq:Line+ {layout(align-list sq)}
|
||||
|
||||
Ex+ = Ex+ ln:Ex {layout(offside ln)}
|
||||
Line = OptTermEx
|
||||
Line.ISequence = TermExLst OptTermEx {layout(0.first.line == 1.first.line)}
|
||||
|
||||
TermExLst.Prior = TermEx+
|
||||
|
||||
OptTermEx = ex:Ex {layout(offside ex)}
|
||||
OptTermEx = te:TermEx {layout(offside te)}
|
||||
|
||||
TermEx.Terminate = <<Ex>;>
|
||||
|
||||
Ex.Int = INT
|
||||
Ex.Stream = <stream>
|
||||
@ -36,8 +44,4 @@ context-free priorities
|
||||
> Ex.Gets,
|
||||
|
||||
// prevent cycle: no singletons
|
||||
LineSeq.Sequence <0> .> Ex+ = Ex,
|
||||
|
||||
// flat: No LineSeq immediately in LineSeq
|
||||
Ex+ = Ex <0> .> LineSeq.Sequence,
|
||||
Ex+ = Ex+ Ex <1> .> LineSeq.Sequence
|
||||
LineSeq.Sequence <0> .> Line+ = Line
|
||||
|
@ -14,8 +14,8 @@ that writes the sum of the ASCII codes for 'H', 'W', and '!' to standard output:
|
||||
|
||||
/** md */ test emit_sum [[
|
||||
stream << 72 + 87 + 33
|
||||
]]/* **/ parse to TopLevel(Gets(Stream(),
|
||||
Sum(Sum(Int("72"), Int("87")), Int("33"))))
|
||||
]] /* **/
|
||||
parse to TopLevel(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))))
|
||||
/** writes
|
||||
192**/
|
||||
|
||||
@ -59,7 +59,8 @@ is also left-associative, so that way we can chain insertions into a stream:
|
||||
|
||||
/** md */ test emit_twice [[
|
||||
stream << 72 + 87 + 33 << 291
|
||||
]]/* **/ parse to TopLevel(
|
||||
]] /* **/
|
||||
parse to TopLevel(
|
||||
Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291")))
|
||||
/** writes
|
||||
192291**/
|
||||
@ -76,7 +77,8 @@ with `>>`; both forms return the first argument, so the following writes "824":
|
||||
|
||||
/** md */ test enters_twice [[
|
||||
(7 + 8 >> stream + 9) >> stream
|
||||
]]/* **/ parse to TopLevel(
|
||||
]] /* **/
|
||||
parse to TopLevel(
|
||||
To(Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9")), Stream()))
|
||||
/** writes
|
||||
824**/
|
||||
@ -96,7 +98,8 @@ stream <<
|
||||
7
|
||||
+ 8 >> stream
|
||||
+ 9
|
||||
]]/* **/ parse to TopLevel(
|
||||
]] /* **/
|
||||
parse to TopLevel(
|
||||
Gets(Stream(), Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9"))))
|
||||
/** writes
|
||||
824**/
|
||||
@ -107,15 +110,17 @@ stream <<
|
||||
```fostr
|
||||
**/
|
||||
|
||||
/** md */ test enter_receive_bad_break [[
|
||||
/** md */ test enter_receive_bad_continuation [[
|
||||
(7 + 8 >> stream + 9)
|
||||
>> (stream << 9 + 2)
|
||||
]] /* **/ parse fails
|
||||
]] /* **/
|
||||
parse fails
|
||||
|
||||
/* Extra tests not in the tour */
|
||||
test enter_receive [[
|
||||
(7 + 8 >> stream + 9) >> (stream << 9 + 2)
|
||||
]]/* **/ parse to TopLevel(
|
||||
]] /* **/
|
||||
parse to TopLevel(
|
||||
To(Sum(Sum(Int("7"),To(Int("8"),Stream())),Int("9")),
|
||||
Gets(Stream(),Sum(Int("9"),Int("2")))))
|
||||
/** writes
|
||||
@ -135,18 +140,20 @@ lines are evaluated in sequence. For example, the program
|
||||
+ 96
|
||||
99 + 12 >>
|
||||
stream
|
||||
]] /* **/ parse to TopLevel( Sequence(
|
||||
[ Gets(Stream(), Sum(Int("72"), Int("87")))
|
||||
, Gets(Stream(), Sum(Int("88"), Int("96")))
|
||||
, Sum(Int("99"), To(Int("12"), Stream()))]))
|
||||
]] /* **/
|
||||
parse to TopLevel(Sequence([
|
||||
Gets(Stream(), Sum(Int("72"), Int("87"))),
|
||||
Gets(Stream(), Sum(Int("88"), Int("96"))),
|
||||
Sum(Int("99"), To(Int("12"), Stream()))
|
||||
]))
|
||||
/** writes
|
||||
15918412**/
|
||||
|
||||
/** md
|
||||
```
|
||||
|
||||
will write 15918412. fostr enforces that successive expressions in sequence
|
||||
must line up at the left, i.e., the following will not parse:
|
||||
will write 15918412. The fostr parser enforces that successive expressions
|
||||
in sequence align at the left; e.g., the following fails to parse:
|
||||
|
||||
```fostr
|
||||
**/
|
||||
@ -156,8 +163,61 @@ must line up at the left, i.e., the following will not parse:
|
||||
stream << 88
|
||||
+ 96
|
||||
99 + 12 >> stream
|
||||
]] /* **/ parse fails
|
||||
]] /* **/
|
||||
parse fails
|
||||
|
||||
/** md
|
||||
```
|
||||
|
||||
Note you can optionally terminate an expression in a sequence with a semicolon,
|
||||
and you may place multiple expressions on a single line if the earlier one(s)
|
||||
are so terminated. So the following is OK:
|
||||
|
||||
```fostr
|
||||
**/
|
||||
|
||||
/** md */ test emit_several [[
|
||||
stream << 1 + 2; 3 >> stream
|
||||
(4 + 5) >> stream; stream << 6;
|
||||
stream << 7
|
||||
stream << 8
|
||||
+ (9+10);
|
||||
11 + 12 >> stream; 13 >> stream
|
||||
>> stream
|
||||
]] /* **/
|
||||
parse to TopLevel(Sequence([
|
||||
ISequence(Prior([Terminate(Gets(Stream(), Sum(Int("1"), Int("2"))))]),
|
||||
To(Int("3"), Stream())),
|
||||
ISequence(Prior([Terminate(To(Sum(Int("4"), Int("5")), Stream()))]),
|
||||
Terminate(Gets(Stream(), Int("6")))),
|
||||
Gets(Stream(), Int("7")),
|
||||
Terminate(Gets(Stream(), Sum(Int("8"), Sum(Int("9"), Int("10"))))),
|
||||
ISequence(Prior([Terminate(Sum(Int("11"), To(Int("12"), Stream())))]),
|
||||
To(To(Int("13"), Stream()), Stream()))
|
||||
]))
|
||||
/** writes
|
||||
3396727121313**/
|
||||
|
||||
/** md
|
||||
```
|
||||
**/
|
||||
|
||||
test emit_several_desugar [[
|
||||
stream << 1 + 2; 3 >> stream
|
||||
(4 + 5) >> stream; stream << 6;
|
||||
stream << 7
|
||||
stream << 8
|
||||
+ (9+10);
|
||||
11 + 12 >> stream; 13 >> stream
|
||||
>> stream
|
||||
]] /* don't test */
|
||||
run desugar-fostr to TopLevel(Sequence([
|
||||
Terminate(Gets(Stream(), Sum(Int("1"), Int("2")))),
|
||||
To(Int("3"), Stream()),
|
||||
Terminate(To(Sum(Int("4"), Int("5")), Stream())),
|
||||
Terminate(Gets(Stream(), Int("6"))),
|
||||
Gets(Stream(), Int("7")),
|
||||
Terminate(Gets(Stream(), Sum(Int("8"), Sum(Int("9"), Int("10"))))),
|
||||
Terminate(Sum(Int("11"), To(Int("12"), Stream()))),
|
||||
To(To(Int("13"), Stream()), Stream())
|
||||
]))
|
||||
|
7
tests/emit_several.fos
Normal file
7
tests/emit_several.fos
Normal file
@ -0,0 +1,7 @@
|
||||
stream << 1 + 2; 3 >> stream
|
||||
(4 + 5) >> stream; stream << 6;
|
||||
stream << 7
|
||||
stream << 8
|
||||
+ (9+10);
|
||||
11 + 12 >> stream; 13 >> stream
|
||||
>> stream
|
@ -9,6 +9,7 @@ imports
|
||||
injections/-
|
||||
|
||||
libspoofax/term/origin
|
||||
desugar
|
||||
|
||||
rules // Analysis
|
||||
|
||||
@ -19,7 +20,8 @@ rules // Analysis
|
||||
// multi-file analysis
|
||||
// editor-analyze = stx-editor-analyze(pre-analyze, post-analyze|"statics", "projectOk", "fileOk")
|
||||
|
||||
pre-analyze = origin-track-forced(explicate-injections-fostr-Start)
|
||||
pre-analyze = desugar-fostr
|
||||
; origin-track-forced(explicate-injections-fostr-Start)
|
||||
post-analyze = origin-track-forced(implicate-injections-fostr-Start)
|
||||
|
||||
rules // Editor Services
|
||||
@ -31,16 +33,21 @@ rules // Editor Services
|
||||
rules // Debugging
|
||||
|
||||
// Prints the abstract syntax ATerm of a selection.
|
||||
debug-show-aterm: (selected, _, _, path, project-path) -> (filename, result)
|
||||
debug-show-aterm: (sel, _, _, path, projp) -> (filename, result)
|
||||
with filename := <guarantee-extension(|"aterm")> path
|
||||
; result := selected
|
||||
; result := sel
|
||||
|
||||
// Prints the desugared abstract syntax ATerm of a selection.
|
||||
debug-desugar-fostr: (sel, _, _, path, projp) -> (filename, result)
|
||||
with filename := <guarantee-extension(|"desugared.aterm")> path
|
||||
; result := <desugar-fostr> sel
|
||||
|
||||
// Prints the pre-analyzed abstract syntax ATerm of a selection.
|
||||
debug-show-pre-analyzed: (selected, _, _, path, project-path) -> (filename, result)
|
||||
debug-show-pre-analyzed: (sel, _, _, path, projp) -> (filename, result)
|
||||
with filename := <guarantee-extension(|"pre-analyzed.aterm")> path
|
||||
; result := <pre-analyze> selected
|
||||
; result := <pre-analyze> sel
|
||||
|
||||
// Prints the analyzed annotated abstract syntax ATerm of a selection.
|
||||
debug-show-analyzed: (selected, _, _, path, project-path) -> (filename, result)
|
||||
debug-show-analyzed: (sel, _, _, path, projp) -> (filename, result)
|
||||
with filename := <guarantee-extension(|"analyzed.aterm")> path
|
||||
; result := selected
|
||||
; result := sel
|
||||
|
22
trans/desugar.str
Normal file
22
trans/desugar.str
Normal file
@ -0,0 +1,22 @@
|
||||
module desugar
|
||||
imports libstrategolib signatures/-
|
||||
|
||||
rules
|
||||
/* ISequence() and Prior() are just noise for more expressions in sequence,
|
||||
put in to get the layout rules right. So we remove them and collapse
|
||||
all occurrence of them into one big Sequence() call on a list.
|
||||
|
||||
This is slightly tricky because there might not be any Sequence() call
|
||||
at the top level, but yet an ISequence(). So we do it in two passes,
|
||||
first converting ISequence()s to Sequence()s, and then collapsing
|
||||
Sequence()s.
|
||||
*/
|
||||
deISe: ISequence(Prior(l),x) -> Sequence(<conc>(l, [x]))
|
||||
|
||||
enList: x -> [x]
|
||||
seqFlatten: Sequence(l) -> Sequence(<mapconcat(?Sequence(<id>) <+ enList)>l)
|
||||
|
||||
strategies
|
||||
|
||||
desugar-fostr = bottomup(try(deISe <+ seqFlatten))
|
||||
|
@ -1,10 +1,5 @@
|
||||
module haskell
|
||||
imports libstrategolib signatures/- util
|
||||
|
||||
signature
|
||||
constructors
|
||||
TopLevel: Ex -> Ex
|
||||
|
||||
rules
|
||||
/* Approach: Generate code from the bottom up.
|
||||
At every node, we create a pair of the implementation and
|
||||
@ -25,20 +20,20 @@ rules
|
||||
main = do
|
||||
[p]return [c]]
|
||||
|
||||
hs: Stream() -> ("StdIO", "")
|
||||
hs: Int(x) -> (x, "")
|
||||
hs: Stream() -> ("StdIO", "")
|
||||
hs: Int(x) -> (x, "")
|
||||
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((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: 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"])
|
||||
$[[s] `gets` [v]], "\n"])
|
||||
|
||||
hs: Sequence(l) -> (<last; Fst>l, <map(Snd); concat-strings>l)
|
||||
hs: Terminate((c,p)) -> ($[[c];;], p)
|
||||
hs: Sequence(l) -> (<last; Fst>l, <map(Snd); concat-strings>l)
|
||||
|
||||
strategies
|
||||
|
||||
|
@ -1,10 +1,6 @@
|
||||
module javascript
|
||||
imports libstrategolib signatures/- util
|
||||
|
||||
signature
|
||||
constructors
|
||||
TopLevel: Ex -> Ex
|
||||
|
||||
rules
|
||||
js: TopLevel(x) -> $[const Stdio = {
|
||||
gets: v => { process.stdout.write(String(v)); return Stdio; },
|
||||
@ -15,12 +11,13 @@ rules
|
||||
}
|
||||
[x]]
|
||||
|
||||
js: Stream() -> $[Stdio]
|
||||
js: Int(x) -> x
|
||||
js: Sum(x,y) -> $[[x] + [y]]
|
||||
js: Gets(x, y) -> $[[x].gets([y])]
|
||||
js: To(x, y) -> $[to([x],[y])]
|
||||
js: Sequence(l) -> <join(|";\n")>l
|
||||
js: Stream() -> $[Stdio]
|
||||
js: Int(x) -> x
|
||||
js: Sum(x,y) -> $[[x] + [y]]
|
||||
js: Gets(x, y) -> $[[x].gets([y])]
|
||||
js: To(x, y) -> $[to([x],[y])]
|
||||
js: Terminate(x) -> x
|
||||
js: Sequence(l) -> <join(|";\n")>l
|
||||
|
||||
strategies
|
||||
|
||||
|
@ -1,10 +1,5 @@
|
||||
module python
|
||||
imports libstrategolib signatures/- util
|
||||
|
||||
signature
|
||||
constructors
|
||||
TopLevel: Ex -> Ex
|
||||
|
||||
rules
|
||||
|
||||
py: TopLevel(x) -> $[import sys
|
||||
@ -18,12 +13,13 @@ rules
|
||||
Stdio = StdioC()
|
||||
[x]]
|
||||
|
||||
py: Stream() -> $[Stdio]
|
||||
py: Int(x) -> x
|
||||
py: Sum(x,y) -> $[[x] + [y]]
|
||||
py: Gets(x, y) -> $[[x].gets([y])]
|
||||
py: To(x, y) -> $[to([x],[y])]
|
||||
py: Sequence(l) -> <join(|"\n")>l
|
||||
py: Stream() -> $[Stdio]
|
||||
py: Int(x) -> x
|
||||
py: Sum(x,y) -> $[[x] + [y]]
|
||||
py: Gets(x, y) -> $[[x].gets([y])]
|
||||
py: To(x, y) -> $[to([x],[y])]
|
||||
py: Terminate(x) -> $[[x];]
|
||||
py: Sequence(l) -> <join(|"\n")>l
|
||||
|
||||
strategies
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user