From 7b00b01856b08897de3eca77b6314eda3e2d0d1f Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 30 Jan 2021 15:37:53 -0800 Subject: [PATCH] chore: Switch to this repository from predecessor --- .drone.yml | 63 +++++++++++++ .gitignore | 9 +- LICENSE | 208 +++++++++++++++++++++++++++++++++++++++++ README.md | 48 +++++++--- bin/extract_tests.xsh | 36 +++++++ bin/fosgen | 71 ++++++++++++++ bin/generate_test_code | 19 ++++ bin/run_tests | 33 +++++++ docs/implementation.md | 16 ++++ editor/Analysis.esv | 2 +- editor/Generation.esv | 6 ++ editor/Main.esv | 1 + editor/Syntax.esv | 2 +- mkdocs.yml | 22 +++++ syntax/fostr.sdf3 | 21 ++++- tests/basic.spt | 74 +++++++++++++++ tests/emit_sum.fos | 1 + trans/analysis.str | 6 +- trans/fostr.str | 3 + trans/haskell.str | 41 ++++++++ trans/javascript.str | 31 ++++++ trans/python.str | 34 +++++++ trans/statics.stx | 18 ++-- 23 files changed, 733 insertions(+), 32 deletions(-) create mode 100644 .drone.yml create mode 100644 LICENSE create mode 100644 bin/extract_tests.xsh create mode 100755 bin/fosgen create mode 100755 bin/generate_test_code create mode 100755 bin/run_tests create mode 100644 docs/implementation.md create mode 100644 editor/Generation.esv create mode 100644 mkdocs.yml create mode 100644 tests/basic.spt create mode 100644 tests/emit_sum.fos create mode 100644 trans/haskell.str create mode 100644 trans/javascript.str create mode 100644 trans/python.str diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..452f163 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,63 @@ +--- +kind: pipeline +name: examples +steps: + - name: build + image: maven + volumes: + - name: lib + path: /drone/lib + - name: m2 + path: /root/.m2 + commands: + - mvn -ntp verify + - cd /drone/lib + - git clone https://github.com/metaborg/spt.git + - cd spt/org.metaborg.spt.cmd + - mvn -ntp package + - name: run_spt + image: maven + volumes: + - name: lib + path: /drone/lib + - name: m2 + path: /root/.m2 + commands: + - cd /drone/lib/spt/org.metaborg.meta.lang.spt + - mvn -ntp verify + - cd /drone/src + - 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 + - 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' + - bin/fosgen tests/emit_sum.fos + - name: extract_tests + image: xonsh/xonsh + commands: + - xonsh bin/extract_tests.xsh + - name: generate_tests + image: maven + volumes: + - name: lib + path: /drone/lib + - name: m2 + path: /root/.m2 + commands: + - bin/generate_test_code + - name: python_tests + image: python:slim + commands: + - bin/run_tests python py + - name: javascript_tests + image: node + commands: + - bin/run_tests node js + - name: haskell_tests + image: haskell + commands: + - bin/run_tests runghc hs + +volumes: + - name: lib + temp: {} + - name: m2 + temp: {} diff --git a/.gitignore b/.gitignore index d0d8774..cd46533 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /.cache -/bin +/lib /src-gen /target @@ -9,3 +9,10 @@ /.factorypath /.polyglot.metaborg.yaml + +*.aterm +/site +tests/extracted/* +tests/*.js +tests/*.py +tests/*.hs diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ed90b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. diff --git a/README.md b/README.md index 2c62c8a..010be66 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,40 @@ -# fostr Language Specification +# The fostr programming language -## Using Statix for multi-file analysis +I don't really like to write code, but I do like the things that coding can +build for me: accounting systems for non-profits I care about, spreadsheets +that have a reasonable calculation language for cell contents, geometric +visualizations that really help to understand three (and maybe even four!) +dimensions. -By default the project is configured to analyze all files of your language in -isolation -- single-file analysis. It is also possible to configure the project -such that all files are analyzed together, and files can refer to each other -- -multi-file analysis. +So I embarked on this project to see if I could produce as comfortable a +language as possible to work in, given that I inevitably will be doing a +bunch of coding. The language will be +organized around (unary) ++f++unctions, (binary) ++o++perators, and +(nullary) ++str++eams, hence the name "fostr". -To enable multi-file analysis, do the following: -1. Uncomment the `(multifile)` option in `editor/Analysis.esv` -2. Uncomment the multi-file definition, and comment the single-file version, of - `editor-analyze` in `trans/analysis.str`. +Other guiding principles: -NB. When working in an IDE such as Eclipse, it is necessary to _restart the IDE_ -after switching from single-file to multi-file analysis or vice versa. Failure to -do so will result in exceptions during analysis. +* Maximize signal to noise ratio in code, which means minimizing the number + of symbols that have to be there just for the syntax; reducing punctuation; + seeking brief syntax that is not too terse; etc. +* Since code is always structurally indented anyway, make use of that and + don't repeat information that's in the whitespace. This principle meshes + well with the previous one, and if whitespace significance is baked into + the language design from the ground up, it can be kept both effective and + natural. + +* Code uses functions all the time. So needless to say, functions should be + first-class entities that are exceptionally easy to create, pass around, + etc. + +* And true to the name, operators and streams should be just as easy to handle. + +* Try to keep the constructs available as simple to reason about as possible, + and practical to use. So side effects are OK, and it should be clear when + they occur and in what order. And if possible, fostr will consist **only** + of expressions, no other syntactic constructs. Everything has a value. + + +Like just about every other language, this documentation begins with a +[whirlwind tour](http::/studioinfinity.org/fostr/basic). diff --git a/bin/extract_tests.xsh b/bin/extract_tests.xsh new file mode 100644 index 0000000..40ead93 --- /dev/null +++ b/bin/extract_tests.xsh @@ -0,0 +1,36 @@ +#!/usr/bin/env xonsh + +import re + +#### Set Parameters +# Should be a list of Path objects: +TEST_LIST = pg`tests/*.spt` + +# Should be the top-level directory for all extracted tests: +# (there will be one subdirectory per item in TEST_LIST) +DESTINATION = 'tests/extracted' + +# Extension for extracted files: +EXT = 'fos' + +# Extension for expectations: +EXP = 'expect' + +for path in TEST_LIST: + destdir = pf"{DESTINATION}/{path.stem}" + mkdir -p @(destdir) + contents = path.read_text() + tests = re.split(r'test\s*(.+?)\s*\[\[.*?\n', contents)[1:] + testit = iter(tests) + for name, details in zip(testit, testit): + em = re.search(r'\n\s*\]\]', details) + if not em: continue + example = details[:em.start()+1] + expath = destdir / f"{name}.{EXT}" + expath.write_text(example) + echo Wrote @(expath) + xm = re.search(r'/\*\*\s+writes.*?\n([\s\S]*?)\*\*/', details[em.end():]) + if xm: + xpath = destdir / f"{name}.{EXP}" + xpath.write_text(xm[1]) + echo " ...and" @(xpath) diff --git a/bin/fosgen b/bin/fosgen new file mode 100755 index 0000000..c5af48b --- /dev/null +++ b/bin/fosgen @@ -0,0 +1,71 @@ +#!/bin/bash + +##### Convenience: +erro() { printf "%s\n" "$*" >&2; } + +##### Set defaults: +SUPPRESS_ERR=YES +LANGUAGE=Python + +##### Extract command line options: +while [[ $1 = -* ]] +do + case $1 in + -h|--help) + echo + echo "Usage:" + echo " fosgen [-d] [-l LANGUAGE] FILE" + echo + echo "Writes to standard output the code generated from the fostr" + echo "program in FILE, targeting the specified LANGUAGE (which" + echo "defaults to Python)." + echo + echo "The -d option writes diagnostic output to standard error." + exit + ;; + -d) + SUPPRESS_ERR='' + ;; + -l) + shift + LANGUAGE="$1" + ;; + esac + shift +done + +##### Get positional arguments: +PROGRAM=$1 + +##### Corral resources: +BINDIR=$(dirname $BASH_SOURCE) +SUNJAR="$BINDIR/../lib/sunshine.jar" +PROJDIR="$BINDIR/.." + +if [[ ! -f $SUNJAR ]]; then + erro "Please download the Spoofax Sunshine jar to the lib directory." + erro "Recommended command:" + erro " 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'" + exit 1 +fi + +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 + erro "Cannot find your Maven repository. Please set environment variable" + erro "MVN_REPO to its full path and re-run." + exit 1 +fi + +##### Perform the code generation: +if [[ $SUPPRESS_ERR ]] +then + exec 2>/dev/null +fi + +java -jar $SUNJAR transform -p $PROJDIR -l $PROJDIR -l $MVN_REPO -n $LANGUAGE -i $PROGRAM +exit $? diff --git a/bin/generate_test_code b/bin/generate_test_code new file mode 100755 index 0000000..2c443d6 --- /dev/null +++ b/bin/generate_test_code @@ -0,0 +1,19 @@ +#!/bin/bash + +failed=0 + +for dir in tests/extracted/*; do + for file in $dir/*.fos; do + for language in Python Javascript Haskell; do + echo bin/fosgen -l ${language%.*} $file ... + bin/fosgen -l $language $file + if [[ $? -ne 0 ]]; then + echo ' ... Failed.' + ((failed++)) + fi + done + done +done + +echo "Code generation completed with $failed failures." +exit $failed diff --git a/bin/run_tests b/bin/run_tests new file mode 100755 index 0000000..90ccaa7 --- /dev/null +++ b/bin/run_tests @@ -0,0 +1,33 @@ +#!/bin/bash + +command=$1 +ext=$2 +total=0 +failed=0 +diffed=0 + +for dir in tests/extracted/*; do + for file in $dir/*.$ext; do + ((total++)) + $command $file > $file.out + if [[ $? -ne 0 ]]; then + echo ERROR: $command $file failed. + ((failed++)) + else + if [[ -f ${file%.*}.expect ]]; then + echo ---- diff ${file%.*}.expect $file.out ---- + diff ${file%.*}.expect $file.out + if [[ $? -ne 0 ]]; then + ((diffed++)) + fi + echo ------------------------------- + fi + fi + done +done + +echo "Ran $total tests: $failed failures, $diffed runs differed." +if [[ $failed -gt 0 ]] || [[ $diffed -gt 0 ]]; then + exit 1 +fi +exit 0 diff --git a/docs/implementation.md b/docs/implementation.md new file mode 100644 index 0000000..297bd99 --- /dev/null +++ b/docs/implementation.md @@ -0,0 +1,16 @@ +## Using Statix for multi-file analysis + +By default the project is configured to analyze all files of your language in +isolation -- single-file analysis. It is also possible to configure the project +such that all files are analyzed together, and files can refer to each other -- +multi-file analysis. + +To enable multi-file analysis, do the following: +1. Uncomment the `(multifile)` option in `editor/Analysis.esv` +2. Uncomment the multi-file definition, and comment the single-file version, of + `editor-analyze` in `trans/analysis.str`. + +NB. When working in an IDE such as Eclipse, it is necessary to _restart the IDE_ +after switching from single-file to multi-file analysis or vice versa. Failure to +do so will result in exceptions during analysis. + diff --git a/editor/Analysis.esv b/editor/Analysis.esv index ba43800..0667f1e 100644 --- a/editor/Analysis.esv +++ b/editor/Analysis.esv @@ -6,7 +6,7 @@ imports language - // see README.md for details on how to switch to multi-file analysis + // see docs/implementation.md for details on how to switch to multi-file analysis observer : editor-analyze (constraint) // (multifile) diff --git a/editor/Generation.esv b/editor/Generation.esv new file mode 100644 index 0000000..3d71fe2 --- /dev/null +++ b/editor/Generation.esv @@ -0,0 +1,6 @@ +module Generation +menus + menu: "Generation" (openeditor) + action: "Python" = to-python + action: "Javascript" = to-javascript + action: "Haskell" = to-haskell diff --git a/editor/Main.esv b/editor/Main.esv index 116630c..7feadde 100644 --- a/editor/Main.esv +++ b/editor/Main.esv @@ -4,6 +4,7 @@ imports Syntax Analysis + Generation language diff --git a/editor/Syntax.esv b/editor/Syntax.esv index aad195c..e8e0e23 100644 --- a/editor/Syntax.esv +++ b/editor/Syntax.esv @@ -8,7 +8,7 @@ imports language table : target/metaborg/sdf.tbl - start symbols : Start + start symbols : Ex line comment : "//" block comment : "/*" * "*/" diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..366fe2c --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,22 @@ +site_name: fostr language +nav: +- README.md +- tests/basic.md +- implementation.md + +plugins: +- search +- semiliterate: + ignore_folders: [target] + exclude_extensions: ['.o', '.hi'] + extract_standard_markdown: + terminate: +theme: + name: readthedocs +markdown_extensions: +- attr_list +- markdown-del-ins +- pymdownx.superfences +- pymdownx.highlight: + use_pygments: true +- smarty diff --git a/syntax/fostr.sdf3 b/syntax/fostr.sdf3 index 01493a0..0f52846 100644 --- a/syntax/fostr.sdf3 +++ b/syntax/fostr.sdf3 @@ -6,12 +6,27 @@ imports context-free start-symbols - Start + Ex context-free sorts - Start + Ex context-free syntax - Start.Empty = <> + Ex.Int = INT + Ex.Stdio = + Ex.Sum = {Ex "+"}+ + Ex.Receives = [[Ex] << [Ex]] {left} + +context-free priorities + + Ex.Sum + > Ex.Receives, + + // prevent cycle: no singletons + Ex.Sum <0> .> {Ex "+"}+ = Ex, + + // flat: no Sum immediately in Sum: + {Ex "+"}+ = Ex <0> .> Ex.Sum, + {Ex "+"}+ = {Ex "+"}+ "+" Ex <2> .> Ex.Sum diff --git a/tests/basic.spt b/tests/basic.spt new file mode 100644 index 0000000..096b213 --- /dev/null +++ b/tests/basic.spt @@ -0,0 +1,74 @@ +module basic +language fostr + +/** md +Title: A whirlwind tour of fostr + +## Whirlwind tour + +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 +**/ + +/** md */ test emit_sum [[ +stdio << 72 + 87 + 33 +]]/* **/ parse to Receives(Stdio(), Sum([Int("72"), Int("87"), Int("33")])) +/** writes +192**/ + +/** md +``` + +At the moment, there are only two ways to run a file containing fostr code +(you can find the above in `tests/emit_sum.fos`). They both start by +cloning this fostr project. Then, either: + +1. Open the project in Eclipse and build it, visit your program file, + generate code from it in your preferred target language (among + the options available in the "Spoofax > Generate" menu), and execute the + resulting code. + +1. Use the `bin/fosgen` bash script to generate code in a target language, + and execute the resulting code. + +For example, this snippet generates the following Python: +```python +{! ../examples/emit_sum.py extract: + start: 'Stdio\s=' +!} +``` +(which writes "192" to standard output), or this non-idiomatic, inefficient, but +working Javascript: +```javascript +{! ../examples/emit_sum.js extract: + start: '^}' +!} +``` +In either case, there's also a preamble defining Stdio that's generated. +(Haskell code generation is also currently supported.) + +### Everything has a value + +As mentioned in the [Introduction](../README.md), everything in a fostr +program (including the entire program itself) is an expression and has +a value. So what's the value of that expression above? Well, `stdio` is our +first example of a stream, and for convenience, the value of a stream +receiving an item is just the stream back again. The `<<` operator is also +left-associative, so that way we can chain insertions into a stream: +```fostr +**/ + +/** md */ test emit_twice [[ +stdio << 72 + 87 + 33 << 291 +]]/* **/ parse to Receives( + Receives(Stdio(), Sum([Int("72"), Int("87"), Int("33")])), + Int("291")) +/** writes +192291**/ + +/** md +``` +Running this program produces a nice palindromic output: "192291". +**/ diff --git a/tests/emit_sum.fos b/tests/emit_sum.fos new file mode 100644 index 0000000..8dfe808 --- /dev/null +++ b/tests/emit_sum.fos @@ -0,0 +1 @@ +stdio << 72 + 87 + 33 diff --git a/trans/analysis.str b/trans/analysis.str index 75a5b1e..a28f21a 100644 --- a/trans/analysis.str +++ b/trans/analysis.str @@ -15,12 +15,12 @@ rules // Analysis // single-file analysis editor-analyze = stx-editor-analyze(pre-analyze, post-analyze|"statics", "programOk") - // see README.md for details on how to switch to multi-file analysis + // see docs/implementation.md for details on how to switch to multi-file 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) - post-analyze = origin-track-forced(implicate-injections-fostr-Start) + pre-analyze = origin-track-forced(explicate-injections-fostr-Ex) + post-analyze = origin-track-forced(implicate-injections-fostr-Ex) rules // Editor Services diff --git a/trans/fostr.str b/trans/fostr.str index 3b56116..d9a585a 100644 --- a/trans/fostr.str +++ b/trans/fostr.str @@ -6,6 +6,9 @@ imports pp outline analysis + haskell + javascript + python rules // Debugging diff --git a/trans/haskell.str b/trans/haskell.str new file mode 100644 index 0000000..afa5a6a --- /dev/null +++ b/trans/haskell.str @@ -0,0 +1,41 @@ +module haskell +imports libstrategolib signatures/- + +signature + constructors + TopLevel: Ex -> Ex + +rules + hs: TopLevel(x) -> $[import System.IO + data IOStream = StdIO + + stdio :: IO IOStream + stdio = return StdIO + + receives :: Show b => IO a -> b -> IO a + receives s d = do + temp <- s + putStr(show d) + return temp + + main = do + [x]] + + hs: Stdio() -> $[stdio] + hs: Int(x) -> x + hs: Sum(x) -> $[sum [x]] + hs: Receives(x, y) -> $[[x] `receives` [y]] + hs: [] -> $<[]> + hs: [x | xs] -> $<[<x><xs>]> + +strategies + // wrap expression in a toplevel and then apply code generation + haskell = !TopLevel(); hs + + // translate each element of a list, prepending each with ',', and concatenate + hstail = foldr(!"", \ (x,y) -> $<, <x>> \) + + // Interface haskell code generation with editor services and file system + to-haskell: (selected, _, _, path, project-path) -> (filename, result) + with filename := path + ; result := selected diff --git a/trans/javascript.str b/trans/javascript.str new file mode 100644 index 0000000..d720942 --- /dev/null +++ b/trans/javascript.str @@ -0,0 +1,31 @@ +module javascript +imports libstrategolib signatures/- + +signature + constructors + TopLevel: Ex -> Ex + +rules + js: TopLevel(x) -> $[const Stdio = { + receives: v => { process.stdout.write(String(v)); return Stdio; } + } + [x]] + + js: Stdio() -> $[Stdio] + js: Int(x) -> x + js: Sum(x) -> $[[x].reduce((v,w) => v+w)] + js: Receives(x, y) -> $[[x].receives([y])] + js: [] -> $<[]> + js: [x | xs] -> $<[<x><xs>]> + +strategies + // wrap expression in a toplevel and then apply code generation + javascript = !TopLevel(); js + + // translate each element of a list, prepending each with ',', and concatenate + jstail = foldr(!"", \ (x,y) -> $<, <x>> \) + + // Interface javascript code generation with editor services and file system + to-javascript: (selected, _, _, path, project-path) -> (filename, result) + with filename := path + ; result := selected diff --git a/trans/python.str b/trans/python.str new file mode 100644 index 0000000..cea2202 --- /dev/null +++ b/trans/python.str @@ -0,0 +1,34 @@ +module python +imports libstrategolib signatures/- + +signature + constructors + TopLevel: Ex -> Ex + +rules + py: TopLevel(x) -> $[import sys + class StdioC: + def receives(self, v): + print(v, file=sys.stdout, end='') + return self + Stdio = StdioC() + [x]] + + py: Stdio() -> $[Stdio] + py: Int(x) -> x + py: Sum(x) -> $[sum([x])] + py: Receives(x, y) -> $[[x].receives([y])] + py: [] -> $<[]> + py: [x | xs] -> $<[<x><xs>]> + +strategies + // wrap expression in a toplevel and then apply code generation + python = !TopLevel(); py + + // translate each element of a list, prepending each with ',', and concatenate + pytail = foldr(!"", \ (x,y) -> $[, [x][y]] \) + + // Interface python code generation with editor services and file system + to-python: (selected, _, _, path, project-path) -> (filename, result) + with filename := path + ; result := selected diff --git a/trans/statics.stx b/trans/statics.stx index a0b0c6d..b7385e2 100644 --- a/trans/statics.stx +++ b/trans/statics.stx @@ -1,12 +1,15 @@ module statics -// see README.md for details on how to switch to multi-file analysis +imports signatures/fostr-sig + +// see docs/implementation.md for details on how to switch to multi-file analysis rules // single-file entry point - programOk : Start + programOk : Ex - programOk(Empty()). + programOk(Sum(_)). + programOk(Receives(_,_)). rules // multi-file entry point @@ -14,11 +17,6 @@ rules // multi-file entry point projectOk(s). - fileOk : scope * Start + fileOk : scope * Ex - fileOk(s, Empty()). - -signature - - sorts Start constructors - Empty : Start + fileOk(s, Receives(_,_)).