├── .dockerignore
├── .gitattributes
├── .gitignore
├── .travis.yml
├── 3rdparty
└── jvm
│ └── BUILD
├── BUILD.tools
├── Dockerfile
├── LICENSE
├── README.md
├── build-support
├── ivy
│ ├── BUILD.netrc
│ ├── pgp.ivysettings.xml
│ ├── publish.ivysettings.xml
│ └── pushdb
│ │ └── com.madavan
│ │ ├── caustic-common_2.12
│ │ └── publish.properties
│ │ ├── caustic-compiler_2.12
│ │ └── publish.properties
│ │ ├── caustic-grammar
│ │ └── publish.properties
│ │ ├── caustic-library_2.12
│ │ └── publish.properties
│ │ ├── caustic-runtime_2.12
│ │ └── publish.properties
│ │ ├── caustic-service_2.12
│ │ └── publish.properties
│ │ └── caustic-thrift
│ │ └── publish.properties
├── pants
│ └── publish
│ │ ├── __init__.py
│ │ └── register.py
└── travis
│ └── pants.ini
├── caustic-assets
├── README.md
├── images
│ ├── caustic-banner.png
│ ├── caustic-logo.png
│ ├── runtime-latency.png
│ ├── runtime-throughput.png
│ ├── ycsb-runtime.png
│ └── ycsb-throughput.png
├── results
│ ├── profile
│ │ ├── jprofiler_images
│ │ │ ├── ff00c400_bff000000.png
│ │ │ ├── hotspot_16.png
│ │ │ ├── pixel_ff800000.png
│ │ │ ├── selector_group_16.png
│ │ │ └── tree
│ │ │ │ ├── menu_bar_18.gif
│ │ │ │ ├── menu_corner_18.gif
│ │ │ │ ├── menu_corner_minus_18.gif
│ │ │ │ ├── menu_corner_plus_18.gif
│ │ │ │ ├── menu_tee_18.gif
│ │ │ │ ├── menu_tee_minus_18.gif
│ │ │ │ ├── menu_tee_plus_18.gif
│ │ │ │ └── pixel_transparent_1.gif
│ │ └── mysql-execute.html
│ └── runtime
│ │ ├── css
│ │ ├── bootstrap-slider.css
│ │ ├── bootstrap.min.css
│ │ ├── icons.gif
│ │ ├── index.css
│ │ ├── jquery-ui-1.10.3.custom.css
│ │ ├── ui.dynatree.css
│ │ └── vline.gif
│ │ ├── img
│ │ ├── arrow.png
│ │ └── glyphicons-halflings.png
│ │ ├── index.html
│ │ ├── js
│ │ ├── ScalaMeter
│ │ │ ├── chart.js
│ │ │ ├── data.js
│ │ │ ├── dimensions.js
│ │ │ ├── filter.js
│ │ │ ├── helper.js
│ │ │ ├── main.js
│ │ │ └── permalink.js
│ │ ├── bootstrap.min.js
│ │ ├── crossfilter.min.js
│ │ ├── d3.v3.min.js
│ │ ├── jquery-1.9.1.js
│ │ ├── jquery-compat.js
│ │ ├── jquery-ui-1.10.3.custom.min.js
│ │ └── jquery.dynatree.js
│ │ └── report
│ │ ├── css
│ │ ├── bootstrap-slider.css
│ │ ├── bootstrap.min.css
│ │ ├── icons.gif
│ │ ├── index.css
│ │ ├── jquery-ui-1.10.3.custom.css
│ │ ├── ui.dynatree.css
│ │ └── vline.gif
│ │ ├── img
│ │ ├── arrow.png
│ │ └── glyphicons-halflings.png
│ │ ├── index.html
│ │ └── js
│ │ ├── ScalaMeter
│ │ ├── chart.js
│ │ ├── data.js
│ │ ├── dimensions.js
│ │ ├── filter.js
│ │ ├── helper.js
│ │ ├── main.js
│ │ └── permalink.js
│ │ ├── bootstrap.min.js
│ │ ├── crossfilter.min.js
│ │ ├── d3.v3.min.js
│ │ ├── jquery-1.9.1.js
│ │ ├── jquery-compat.js
│ │ ├── jquery-ui-1.10.3.custom.min.js
│ │ └── jquery.dynatree.js
└── review
│ └── main.tex
├── caustic-benchmark
├── README.md
└── src
│ └── main
│ └── scala
│ ├── BUILD
│ └── caustic
│ └── benchmark
│ ├── Suite.scala
│ └── runtime
│ ├── LatencyBenchmark.scala
│ ├── ProfileBenchmark.scala
│ ├── ThroughputBenchmark.scala
│ └── VolumeBenchmark.scala
├── caustic-compiler
├── README.md
└── src
│ ├── main
│ ├── antlr
│ │ ├── BUILD
│ │ └── Caustic.g4
│ ├── python
│ │ ├── BUILD
│ │ └── caustic
│ │ │ ├── __init__.py
│ │ │ └── pants
│ │ │ ├── __init__.py
│ │ │ ├── register.py
│ │ │ ├── targets
│ │ │ ├── __init__.py
│ │ │ └── caustic_library.py
│ │ │ └── tasks
│ │ │ ├── __init__.py
│ │ │ └── caustic_gen.py
│ ├── scala
│ │ ├── BUILD
│ │ └── caustic
│ │ │ └── compiler
│ │ │ ├── Causticc.scala
│ │ │ ├── error
│ │ │ ├── Error.scala
│ │ │ ├── Handler.scala
│ │ │ └── Trace.scala
│ │ │ ├── gen
│ │ │ ├── Gen.scala
│ │ │ ├── GenBlock.scala
│ │ │ ├── GenComment.scala
│ │ │ ├── GenExternal.scala
│ │ │ ├── GenInternal.scala
│ │ │ ├── GenParameters.scala
│ │ │ └── GenType.scala
│ │ │ ├── reflect
│ │ │ ├── Result.scala
│ │ │ ├── Universe.scala
│ │ │ ├── binding.scala
│ │ │ └── package.scala
│ │ │ └── util
│ │ │ ├── Indenter.scala
│ │ │ └── package.scala
│ └── tmbundle
│ │ ├── Preferences
│ │ └── balance.tmPreferences
│ │ ├── Snippets
│ │ ├── if.tmSnippet
│ │ └── while.tmSnippet
│ │ ├── Syntaxes
│ │ └── Caustic.tmLanguage
│ │ └── info.plist
│ └── test
│ └── scala
│ ├── BUILD
│ └── caustic
│ └── compiler
│ └── CausticcTest.scala
├── caustic-example
└── src
│ └── main
│ └── caustic
│ ├── BUILD
│ └── caustic
│ └── example
│ ├── counter.acid
│ ├── fs.acid
│ ├── lock.acid
│ └── queue.acid
├── caustic-library
├── README.md
└── src
│ ├── main
│ └── scala
│ │ ├── BUILD
│ │ └── caustic
│ │ └── library
│ │ ├── Context.scala
│ │ ├── control
│ │ └── package.scala
│ │ ├── math
│ │ └── package.scala
│ │ └── typing
│ │ ├── Constant.scala
│ │ ├── Conversion.scala
│ │ ├── Internal.scala
│ │ ├── Pointer.scala
│ │ ├── Value.scala
│ │ ├── Variable.scala
│ │ ├── collection
│ │ ├── Collection.scala
│ │ ├── List.scala
│ │ ├── Map.scala
│ │ └── Set.scala
│ │ ├── package.scala
│ │ ├── primitive.scala
│ │ └── record
│ │ ├── Field.scala
│ │ ├── Reference.scala
│ │ └── ops
│ │ ├── delete.scala
│ │ ├── equal.scala
│ │ ├── json.scala
│ │ └── move.scala
│ └── test
│ └── scala
│ ├── BUILD
│ └── caustic
│ └── library
│ ├── math
│ └── PackageTest.scala
│ └── typing
│ └── record
│ └── ReferenceTest.scala
├── caustic-runtime
├── README.md
└── src
│ ├── main
│ └── scala
│ │ ├── BUILD
│ │ └── caustic
│ │ └── runtime
│ │ ├── Builder.scala
│ │ ├── Runtime.scala
│ │ ├── Volume.scala
│ │ ├── operator.scala
│ │ ├── package.scala
│ │ └── program.scala
│ └── test
│ └── scala
│ ├── BUILD
│ └── caustic
│ └── runtime
│ ├── BuilderTest.scala
│ └── RuntimeTest.scala
├── pants
├── pants.ini
└── release.sh
/.dockerignore:
--------------------------------------------------------------------------------
1 | ####################################################################################################
2 | # .dockerignore #
3 | ####################################################################################################
4 | # macOS
5 | .DS_Store
6 |
7 | # IntelliJ
8 | .idea/
9 | *.iml
10 |
11 | # Git
12 | .git
13 | .gitignore
14 |
15 | # Pants
16 | .cache
17 | dist
18 | .pants.d
19 | .pants.workdir.file_lock
20 | .pids
21 |
22 | # Generated
23 | target/
24 |
25 | # Miscellaneous
26 | caustic-assets
27 | LICENSE
28 | README.md
29 | release.sh
30 | .travis.yml
31 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | caustic-assets/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ####################################################################################################
2 | # .gitignore #
3 | ####################################################################################################
4 | # macOS
5 | .DS_Store
6 |
7 | # Java
8 | *.class
9 | *.jar
10 |
11 | # Python
12 | *.pyc
13 |
14 | # Vim
15 | .*.swo
16 | .*.swp
17 |
18 | # IntelliJ
19 | .idea/
20 | *.iml
21 |
22 | # Pants
23 | .cache
24 | dist
25 | .pants.d
26 | .pants.workdir.file_lock
27 | .pids
28 |
29 | # Generated
30 | target/
31 |
32 | # LaTeX
33 | *.aux
34 | *.bbl
35 | *.bcf
36 | *.blg
37 | *.dvi
38 | *.log
39 | *.out
40 | *.run.xml
41 | *.toc
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ####################################################################################################
2 | # Setup Pants #
3 | # https://github.com/build/pants/blob/master/.travis.yml #
4 | ####################################################################################################
5 | env:
6 | global:
7 | - JAVA_HOME="$HOME/opt/jdk1.8.0_131"
8 | - PANTS_CONFIG_OVERRIDE="['pants.ini', 'build-support/travis/pants.ini']"
9 |
10 | before_cache:
11 | - sudo chown -R travis:travis "${HOME}" "${TRAVIS_BUILD_DIR}"
12 | - find build-support -name "*.py[co]" -delete
13 | - find ${HOME}/.ivy2/pants -type f -name "ivydata-*.properties" -delete
14 | - rm -f ${HOME}/.ivy2/pants/*.{css,properties,xml,xsl}
15 | - rm -rf ${HOME}/.ivy2/pants/com.example
16 | - rm -rf ${HOME}/.cache/pants/stats
17 |
18 | cache:
19 | directories:
20 | - ${HOME}/.cache/pants
21 | - ${HOME}/.ivy2/pants
22 |
23 | ####################################################################################################
24 | # Setup JDK #
25 | # https://github.com/foursquare/fsqio/blob/master/.travis.yml #
26 | ####################################################################################################
27 | before_script: |
28 | if ! ${HOME}/opt/jdk1.8.0_131/bin/java -version; then
29 | mkdir -p ~/opt
30 | wget --no-cookies --no-check-certificate --header \
31 | "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" \
32 | "http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-linux-x64.tar.gz"
33 | tar xzf jdk-8u131-linux-x64.tar.gz -C ~/opt
34 | fi
35 |
36 | ####################################################################################################
37 | # Setup Databases #
38 | # https://docs.travis-ci.com/user/database-setup #
39 | ####################################################################################################
40 | services:
41 | - mysql
42 | - postgresql
43 |
44 | addons:
45 | postgresql: "9.5"
46 |
47 | before_install:
48 | - mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
49 | - psql -c 'CREATE DATABASE test;' -U postgres
50 |
51 | ####################################################################################################
52 | # Compile and Test #
53 | # https://github.com/foursquare/fsqio/blob/master/.travis.yml #
54 | ####################################################################################################
55 | language: python
56 | python:
57 | - '2.7'
58 |
59 | script: |
60 | ./pants compile ::
61 | ./pants test ::
62 |
63 | ####################################################################################################
64 | # Push to Docker #
65 | # https://docs.travis-ci.com/user/docker/ #
66 | ####################################################################################################
67 | services:
68 | - docker
69 |
70 | after_success: |
71 | if [ "$TRAVIS_BRANCH" == "master" ] ; then
72 | docker build -t ashwin153/caustic .
73 | docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
74 | docker push ashwin153/caustic
75 | fi
76 |
--------------------------------------------------------------------------------
/3rdparty/jvm/BUILD:
--------------------------------------------------------------------------------
1 | ####################################################################################################
2 | # Compile #
3 | ####################################################################################################
4 | # Beaker Client
5 | jar_library(name='beaker-client', jars=[
6 | jar(org='com.madavan', name='beaker-client_2.12', rev='2.0.1'),
7 | ])
8 |
9 | # Shapeless
10 | jar_library(name='shapeless', jars=[
11 | jar(org='com.chuusai', name='shapeless_2.12', rev='2.3.2'),
12 | ])
13 |
14 | # SLF4J
15 | jar_library(name='slf4j-simple', jars=[
16 | jar(org='org.slf4j', name='slf4j-simple', rev='1.7.25'),
17 | ])
18 |
19 | # Spray JSON
20 | jar_library(name='spray-json', jars=[
21 | jar(org='io.spray', name='spray-json_2.12', rev='1.3.3'),
22 | ])
23 |
24 | ####################################################################################################
25 | # Build #
26 | ####################################################################################################
27 | # Ammonite
28 | jar_library(name='ammonite', jars=[
29 | jar(org='com.lihaoyi', name='ammonite_2.12.3', rev='1.0.5'),
30 | ])
31 |
32 | # ANTLR
33 | jar_library(name='antlr', jars=[
34 | jar(org='org.antlr', name='antlr4', rev='4.7'),
35 | jar(org='org.antlr', name='antlr4-runtime', rev='4.7'),
36 | ])
37 |
38 | # Causticc
39 | jar_library(name='caustic-compiler', jars=[
40 | jar(org='com.madavan', name='caustic-compiler_2.12', rev='2.0.3'),
41 | ])
42 |
43 | # Scalac
44 | jar_library(name='scala-compiler', dependencies=[':scala-library', ':scala-reflect'], jars=[
45 | jar(org='org.scala-lang', name='scala-compiler', rev='2.12.3'),
46 | ])
47 |
48 | # Scala Library
49 | jar_library(name='scala-library', jars=[
50 | jar(org='org.scala-lang', name='scala-library', rev='2.12.3'),
51 | ])
52 |
53 | # Scala Reflection
54 | jar_library(name='scala-reflect', jars=[
55 | jar(org='org.scala-lang', name='scala-reflect', rev='2.12.3', intransitive=True),
56 | ])
57 |
58 | ####################################################################################################
59 | # Test #
60 | ####################################################################################################
61 | # JUnit
62 | jar_library(name='junit', jars=[
63 | jar(org='junit', name='junit', rev='4.12'),
64 | jar(org='org.pantsbuild', name='junit-runner-annotations', rev='0.0.17'),
65 | ])
66 |
67 | # Mockito
68 | jar_library(name='mockito', jars=[
69 | jar(org='org.mockito', name='mockito-core', rev='2.7.11'),
70 | ])
71 |
72 | # Scalameter
73 | jar_library(name='scalameter', jars=[
74 | jar(org='com.storm-enroute', name='scalameter_2.12', rev='0.8.2')
75 | ])
76 |
77 | # Scalatest
78 | jar_library(name='scalatest', dependencies=[':scala-library', ':scala-reflect'], jars=[
79 | jar(org='org.scalatest', name='scalatest_2.12', rev='3.0.0', excludes=[exclude('org.scala-lang')]),
80 | ])
81 |
--------------------------------------------------------------------------------
/BUILD.tools:
--------------------------------------------------------------------------------
1 | ####################################################################################################
2 | # ANTLR #
3 | # https://git.io/vd1nU #
4 | ####################################################################################################
5 | target(name='antlr-4', dependencies=['3rdparty/jvm:antlr'])
6 |
7 | ####################################################################################################
8 | # Caustic #
9 | # https://github.com/ashwin153/caustic #
10 | ####################################################################################################
11 | target(name='causticc', dependencies=['3rdparty/jvm:caustic-compiler'])
12 |
13 | ####################################################################################################
14 | # Scala #
15 | # https://www.pantsbuild.org/scala.html #
16 | ####################################################################################################
17 | target(name='scalac', dependencies=['3rdparty/jvm:scala-compiler'])
18 | target(name='scala-library', dependencies=['3rdparty/jvm:scala-library'])
19 | target(name='scala-reflect', dependencies=['3rdparty/jvm:scala-reflect'])
20 | target(name='scala-repl', dependencies=['3rdparty/jvm:ammonite'])
21 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:latest
2 | MAINTAINER Ashwin Madavan (ashwin.madavan@gmail.com)
3 |
4 | ####################################################################################################
5 | # Install Pants Dependencies #
6 | # https://github.com/pantsbuild/pants/blob/master/README.md#requirements #
7 | ####################################################################################################
8 | RUN apt-get update && apt-get -y install curl build-essential python python-dev openjdk-8-jdk
9 |
10 | ####################################################################################################
11 | # Compile Caustic #
12 | # Automatically bootstraps Pants and downloads dependencies. #
13 | ####################################################################################################
14 | COPY . /caustic/
15 | RUN cd /caustic && ./pants compile ::
16 | WORKDIR /caustic
17 |
--------------------------------------------------------------------------------
/build-support/ivy/BUILD.netrc:
--------------------------------------------------------------------------------
1 | # Enable ~/.netrc for credentials management of jvm publishing.
2 |
3 | # These credentials are used by our 'public' publish repo defined in [publish]repos in `pants.ini`
4 | # and the username and password here are templated in to a credentials configuration for
5 | # oss.sonatype.org in `build-support/ivy/publish.ivysettings.xml`. This setup is complex, but
6 | # documented well here: http://pantsbuild.org/setup_repo.html#enabling-pants-publish
7 | netrc_credentials(name='netrc')
8 |
--------------------------------------------------------------------------------
/build-support/ivy/pgp.ivysettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/build-support/ivy/publish.ivysettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/build-support/ivy/pushdb/com.madavan/caustic-common_2.12/publish.properties:
--------------------------------------------------------------------------------
1 | revision.major.com.madavan%caustic-common_2.12=1
2 | revision.minor.com.madavan%caustic-common_2.12=3
3 | revision.patch.com.madavan%caustic-common_2.12=1
4 | revision.snapshot.com.madavan%caustic-common_2.12=false
5 | revision.named_is_latest.com.madavan%caustic-common_2.12=false
6 | revision.sha.com.madavan%caustic-common_2.12=6e722a3024cd4398f5f19074a174d5fe0ee4a1d8
7 | revision.fingerprint.com.madavan%caustic-common_2.12=a240d2876d4df0a69470368e10ec57c4c572c73e
8 |
--------------------------------------------------------------------------------
/build-support/ivy/pushdb/com.madavan/caustic-compiler_2.12/publish.properties:
--------------------------------------------------------------------------------
1 | revision.major.com.madavan%caustic-compiler_2.12=2
2 | revision.minor.com.madavan%caustic-compiler_2.12=0
3 | revision.patch.com.madavan%caustic-compiler_2.12=3
4 | revision.snapshot.com.madavan%caustic-compiler_2.12=false
5 | revision.named_is_latest.com.madavan%caustic-compiler_2.12=false
6 | revision.sha.com.madavan%caustic-compiler_2.12=9f3074f4c474364a950606c12d8f6cf50a76dc05
7 | revision.fingerprint.com.madavan%caustic-compiler_2.12=2371aaa48baeb74071a8133a6efb6e8b21b45d45
8 |
--------------------------------------------------------------------------------
/build-support/ivy/pushdb/com.madavan/caustic-grammar/publish.properties:
--------------------------------------------------------------------------------
1 | revision.major.com.madavan%caustic-grammar=2
2 | revision.minor.com.madavan%caustic-grammar=0
3 | revision.patch.com.madavan%caustic-grammar=3
4 | revision.snapshot.com.madavan%caustic-grammar=false
5 | revision.named_is_latest.com.madavan%caustic-grammar=false
6 | revision.sha.com.madavan%caustic-grammar=9f3074f4c474364a950606c12d8f6cf50a76dc05
7 | revision.fingerprint.com.madavan%caustic-grammar=1f66946226d07872e25a10b15dec4928082a77a2
8 |
--------------------------------------------------------------------------------
/build-support/ivy/pushdb/com.madavan/caustic-library_2.12/publish.properties:
--------------------------------------------------------------------------------
1 | revision.major.com.madavan%caustic-library_2.12=2
2 | revision.minor.com.madavan%caustic-library_2.12=0
3 | revision.patch.com.madavan%caustic-library_2.12=3
4 | revision.snapshot.com.madavan%caustic-library_2.12=false
5 | revision.named_is_latest.com.madavan%caustic-library_2.12=false
6 | revision.sha.com.madavan%caustic-library_2.12=9f3074f4c474364a950606c12d8f6cf50a76dc05
7 | revision.fingerprint.com.madavan%caustic-library_2.12=60839b1b0b11945bbd6b91224a4b0cb845b36118
8 |
--------------------------------------------------------------------------------
/build-support/ivy/pushdb/com.madavan/caustic-runtime_2.12/publish.properties:
--------------------------------------------------------------------------------
1 | revision.major.com.madavan%caustic-runtime_2.12=2
2 | revision.minor.com.madavan%caustic-runtime_2.12=0
3 | revision.patch.com.madavan%caustic-runtime_2.12=3
4 | revision.snapshot.com.madavan%caustic-runtime_2.12=false
5 | revision.named_is_latest.com.madavan%caustic-runtime_2.12=false
6 | revision.sha.com.madavan%caustic-runtime_2.12=9f3074f4c474364a950606c12d8f6cf50a76dc05
7 | revision.fingerprint.com.madavan%caustic-runtime_2.12=3ccd9f0fb6b13c4954c9b71ef75c0b514ea487c4
8 |
--------------------------------------------------------------------------------
/build-support/ivy/pushdb/com.madavan/caustic-service_2.12/publish.properties:
--------------------------------------------------------------------------------
1 | revision.major.com.madavan%caustic-service_2.12=1
2 | revision.minor.com.madavan%caustic-service_2.12=4
3 | revision.patch.com.madavan%caustic-service_2.12=1
4 | revision.snapshot.com.madavan%caustic-service_2.12=false
5 | revision.named_is_latest.com.madavan%caustic-service_2.12=false
6 | revision.sha.com.madavan%caustic-service_2.12=77463670b605954acfc8a9940ac4da04a204511f
7 | revision.fingerprint.com.madavan%caustic-service_2.12=96a5ad0770d295d7ae834b68af0f7656d3a0f8b3
8 |
--------------------------------------------------------------------------------
/build-support/ivy/pushdb/com.madavan/caustic-thrift/publish.properties:
--------------------------------------------------------------------------------
1 | revision.major.com.madavan%caustic-thrift=1
2 | revision.minor.com.madavan%caustic-thrift=4
3 | revision.patch.com.madavan%caustic-thrift=1
4 | revision.snapshot.com.madavan%caustic-thrift=false
5 | revision.named_is_latest.com.madavan%caustic-thrift=false
6 | revision.sha.com.madavan%caustic-thrift=77463670b605954acfc8a9940ac4da04a204511f
7 | revision.fingerprint.com.madavan%caustic-thrift=376681a4c390ad73c6fefd801604004d1f2a153b
8 |
--------------------------------------------------------------------------------
/build-support/pants/publish/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/build-support/pants/publish/__init__.py
--------------------------------------------------------------------------------
/build-support/pants/publish/register.py:
--------------------------------------------------------------------------------
1 | # pants/publish/register.py
2 | from pants.build_graph.build_file_aliases import BuildFileAliases
3 | from pants.backend.jvm.repository import Repository
4 | from pants.backend.jvm.ossrh_publication_metadata import (
5 | Developer, License, OSSRHPublicationMetadata, Scm
6 | )
7 |
8 | import os
9 |
10 | repository = Repository(
11 | name='public',
12 | url='https://oss.sonatype.org/#stagingRepositories',
13 | push_db_basedir=os.path.join('build-support', 'ivy', 'pushdb')
14 | )
15 |
16 | metadata = OSSRHPublicationMetadata(
17 | description='Reinventing database transactions',
18 | url='https://github.com/ashwin153/caustic',
19 | licenses=[
20 | License(
21 | name='Apache License, Version 2.0',
22 | url='http://www.apache.org/licenses/LICENSE-2.0'
23 | )
24 | ],
25 | developers=[
26 | Developer(
27 | name='Ashwin Madavan',
28 | email='ashwin.madavan@gmail.com',
29 | url='https://madavan.me'
30 | )
31 | ],
32 | scm=Scm.github(
33 | user='ashwin153',
34 | repo='caustic'
35 | )
36 | )
37 |
38 |
39 | def build_file_aliases():
40 | return BuildFileAliases(objects={
41 | 'public': repository,
42 | 'describe': metadata,
43 | })
44 |
--------------------------------------------------------------------------------
/build-support/travis/pants.ini:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | # Turn off all nailgun use.
3 | use_nailgun: False
4 |
5 | [compile.zinc]
6 | # The Travis free tier OOMs with more than one worker.
7 | worker_count: 1
8 |
9 | [jvm-distributions]
10 | # The JDK install is managed within the .travis.yml.
11 | paths: {
12 | 'linux': [
13 | '%(homedir)s/opt/jdk1.8.0_40',
14 | ],
15 | }
16 |
17 | [test.junit]
18 | parallel_threads: 8
19 |
20 | [test.pytest]
21 | options: ['--duration=3']
22 |
--------------------------------------------------------------------------------
/caustic-assets/README.md:
--------------------------------------------------------------------------------
1 | # References
2 | - [Some Principles of Good Language Design][1],
3 | C. J. Date.
4 | - [Dandelion: A Compiler and Runtime for Heterogeneous Systems][2],
5 | C. J. Rossbach, Y. Yu, J. Currey, J. Martin, D. Fetterly.
6 |
7 | [1]: https://view.publitas.com/a2c3fd5c-c83c-4d8b-a70b-8899d5edbf01/some-principles-of-good-language-design-by-c-j-date/page/6-7
8 | [2]: http://sigops.org/sosp/sosp13/papers/p49-rossbach.pdf
--------------------------------------------------------------------------------
/caustic-assets/images/caustic-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/images/caustic-banner.png
--------------------------------------------------------------------------------
/caustic-assets/images/caustic-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/images/caustic-logo.png
--------------------------------------------------------------------------------
/caustic-assets/images/runtime-latency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/images/runtime-latency.png
--------------------------------------------------------------------------------
/caustic-assets/images/runtime-throughput.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/images/runtime-throughput.png
--------------------------------------------------------------------------------
/caustic-assets/images/ycsb-runtime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/images/ycsb-runtime.png
--------------------------------------------------------------------------------
/caustic-assets/images/ycsb-throughput.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/images/ycsb-throughput.png
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/ff00c400_bff000000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/ff00c400_bff000000.png
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/hotspot_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/hotspot_16.png
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/pixel_ff800000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/pixel_ff800000.png
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/selector_group_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/selector_group_16.png
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/menu_bar_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/menu_bar_18.gif
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/menu_corner_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/menu_corner_18.gif
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/menu_corner_minus_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/menu_corner_minus_18.gif
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/menu_corner_plus_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/menu_corner_plus_18.gif
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/menu_tee_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/menu_tee_18.gif
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/menu_tee_minus_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/menu_tee_minus_18.gif
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/menu_tee_plus_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/menu_tee_plus_18.gif
--------------------------------------------------------------------------------
/caustic-assets/results/profile/jprofiler_images/tree/pixel_transparent_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/profile/jprofiler_images/tree/pixel_transparent_1.gif
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/css/bootstrap-slider.css:
--------------------------------------------------------------------------------
1 | .ui-slider .ui-slider-range {
2 | position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0;
3 | color: #ffffff;
4 | background-color: #0064cd;
5 | background-repeat: repeat-x;
6 | background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));
7 | background-image: -moz-linear-gradient(top, #049cdb, #0064cd);
8 | background-image: -ms-linear-gradient(top, #049cdb, #0064cd);
9 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));
10 | background-image: -webkit-linear-gradient(top, #049cdb, #0064cd);
11 | background-image: -o-linear-gradient(top, #049cdb, #0064cd);
12 | background-image: linear-gradient(top, #049cdb, #0064cd);
13 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);
14 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
15 | border-color: #0064cd #0064cd #003f81;
16 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
17 | }
18 | .ui-widget-header {
19 | font-weight:bold;
20 | border-color: #0064cd #0064cd #003f81;
21 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
22 | border:1px solid #666;
23 | }
24 | .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
25 |
26 | background-color: #e6e6e6;
27 | background-repeat: no-repeat;
28 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));
29 | background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
30 | background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);
31 | background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
32 | background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
33 | background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
34 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
35 |
36 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
37 |
38 | color: #333;
39 | /*font-size: 13px;*/
40 | line-height: normal;
41 | border: 1px solid #ccc;
42 | border-bottom-color: #bbb;
43 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
44 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
45 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
46 | -webkit-transition: 0.1s linear background-image;
47 | -moz-transition: 0.1s linear background-image;
48 | -ms-transition: 0.1s linear background-image;
49 | -o-transition: 0.1s linear background-image;
50 | transition: 0.1s linear background-image;
51 | overflow: visible;
52 |
53 | }
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/css/icons.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/css/icons.gif
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/css/vline.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/css/vline.gif
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/img/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/img/arrow.png
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Performance Report
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
50 |
51 |
52 |
53 |
54 |
55 |
x-axis
56 |
57 |
58 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
97 |
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/js/ScalaMeter/dimensions.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "dimensions" };
3 |
4 | /*
5 | * ----- imports -----
6 | */
7 | var h;
8 |
9 | var keys_,
10 | params_;
11 |
12 | my.init = function() {
13 | h = parent.helper;
14 |
15 | keys_ = [];
16 | params_ = d3.map();
17 | };
18 |
19 | my.addParam = function(key) {
20 | if (!params_.has(key)) {
21 | params_.set(key, addDimension(key, key.substr(h.dKey.paramPrefix.length)));
22 | keys_.push(key);
23 | }
24 | };
25 |
26 | my.filterValues = function(data, legendOrder) {
27 | keys_.forEach(function(key, i) {
28 | var dim = params_.get(key);
29 | dim.filteredValues(h.unique(data, dim.keyFn(), i == 0 ? d3.ascending : legendOrder, true));
30 | });
31 | };
32 |
33 | my.add = function(key) {
34 | var newDim = addDimension(key, key);
35 | params_.set(key, newDim);
36 | keys_.push(key);
37 | return newDim;
38 | }
39 |
40 | my.get = function(key) {
41 | return params_.get(key);
42 | };
43 |
44 | my.getAll = function() {
45 | return keys_.map(function(key) {
46 | return params_.get(key);
47 | });
48 | };
49 |
50 | my.keys = function(_) {
51 | if (!arguments.length) return keys_;
52 | keys_ = _;
53 | };
54 |
55 | function addDimension(key, caption) {
56 | return (function() {
57 | var key_ = key,
58 | caption_ = caption,
59 | selectMode_ = h.selectModes.single,
60 | selectedValues_ = d3.set(),
61 | expanded_ = false,
62 | format_ = h.numberFormat,
63 | keyFn_ = h.mapKey(key),
64 | filterContainer_,
65 | values_,
66 | filteredValues_,
67 | cfDimension_;
68 |
69 | function updateCrossfilter() {
70 | cfDimension_.filterFunction(function(d) {
71 | return d == null || selectedValues_.has(d);
72 | });
73 | }
74 |
75 | function updateSelectMode() {
76 | switch (selectMode_) {
77 | case h.selectModes.single:
78 | var selectedValue = values_[0];
79 | for (var i = 0; i < values_.length; i++) {
80 | if (selectedValues_.has(values_[i])) {
81 | selectedValue = values_[i];
82 | break;
83 | }
84 | }
85 | selectedValues_ = d3.set([selectedValue]);
86 | break;
87 | case h.selectModes.all:
88 | selectedValues_ = d3.set(values_);
89 | break;
90 | }
91 | }
92 |
93 | return {
94 | init: function(data, cfDimension) {
95 | values_ = h.unique(data, h.mapKey(key), d3.ascending);
96 |
97 | // if (key == h.dKey.date) {
98 | // //generate random dates over the past 5 years
99 | // var today = +new Date();
100 | // for(i=0; i<5000; i++){
101 | // values_.push(today - 50 * 86400 * 1000 - Math.round(Math.random() * 5 * 365 * 86400 * 1000));
102 | // }
103 | // values_.sort(d3.ascending);
104 | // }
105 |
106 | selectedValues_ = d3.set(values_);
107 | cfDimension_ = cfDimension;
108 | },
109 |
110 | updateCrossfilter : updateCrossfilter,
111 |
112 | selectMode: function(_) {
113 | if (!arguments.length) return selectMode_;
114 | selectMode_ = _;
115 | updateSelectMode();
116 | },
117 |
118 | selectedValues: function(_) {
119 | if (!arguments.length) return selectedValues_;
120 | selectedValues_ = _;
121 | },
122 |
123 | filteredValues: function(_) {
124 | if (!arguments.length) return filteredValues_;
125 | filteredValues_ = _;
126 | },
127 |
128 | caption: function(_) {
129 | if (!arguments.length) return caption_;
130 | caption_ = _;
131 | },
132 |
133 | format: function(_) {
134 | if (!arguments.length) return format_;
135 | format_ = _;
136 | },
137 |
138 | filterContainer: function(_) {
139 | if (!arguments.length) return filterContainer_;
140 | filterContainer_ = _;
141 | },
142 |
143 | expanded: function(_) {
144 | if (!arguments.length) return expanded_;
145 | expanded_ = _;
146 | },
147 |
148 | key: function() {
149 | return key_;
150 | },
151 |
152 | keyFn: function() {
153 | return keyFn_;
154 | },
155 |
156 | clickValue: function(value) {
157 | if (selectMode_ == h.selectModes.single) {
158 | selectedValues_ = d3.set([value]);
159 | } else {
160 | if (selectedValues_.has(value)) {
161 | selectedValues_.remove(value);
162 | } else {
163 | selectedValues_.add(value);
164 | }
165 | if (selectedValues_.values().length == values_.length) {
166 | selectMode_ = h.selectModes.all;
167 | } else {
168 | selectMode_ = h.selectModes.select;
169 | }
170 | }
171 | },
172 |
173 | getAllValues: function() {
174 | return values_;
175 | },
176 |
177 | selectLast: function() {
178 | selectedValues_ = d3.set([values_[values_.length - 1]]);
179 | }
180 | };
181 | })();
182 | }
183 |
184 | parent[my.name] = my;
185 |
186 | return parent;
187 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/js/ScalaMeter/helper.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "helper" };
3 |
4 | var TSV_DATA_KEYS = {
5 | date: "date",
6 | paramPrefix: "param-",
7 | param: "param-size",
8 | value: "value",
9 | success: "success",
10 | cilo: "cilo",
11 | cihi: "cihi",
12 | complete: "complete",
13 | curve: "curve",
14 | index: "index"
15 | };
16 |
17 | var SELECT_MODES = obj2enum({
18 | single: "Single",
19 | select: "Select",
20 | all: "All"
21 | });
22 |
23 | var LABEL_NULL = "none";
24 |
25 | var DATE_FORMAT = (function(d) {return d3.time.format("%Y-%m-%d %H:%M:%S")(new Date(+d)); });
26 |
27 | var NUMBER_FORMAT = createNumberFormat("\u202F"); // 202F: narrow no-break space
28 |
29 | my.curveKey = mapKey(TSV_DATA_KEYS.curve);
30 |
31 | my.dKey = TSV_DATA_KEYS;
32 |
33 | my.isDef = isDef;
34 |
35 | my.mapKey = mapKey;
36 |
37 | my.selectModes = SELECT_MODES;
38 |
39 | my.dateFormat = DATE_FORMAT;
40 |
41 | my.numberFormat = NUMBER_FORMAT;
42 |
43 | my.mainColors = (function() {
44 | var nGroups = 10;
45 | var groups = d3.scale.category10().domain(d3.range(nGroups));
46 | return function(i) {
47 | return groups(i % nGroups);
48 | };
49 | })();
50 |
51 | my.ident = function(d) {
52 | return d;
53 | };
54 |
55 | my.ascendingToInt = function(a, b) {
56 | return d3.ascending(+a, +b);
57 | };
58 |
59 | my.sortBy = function(fn) {
60 | return function(a, b) {
61 | return d3.ascending(fn(a), fn(b));
62 | };
63 | };
64 |
65 | my.fKey = function(d) {
66 | return d.key;
67 | };
68 |
69 | my.fValue = function(d) {
70 | return d.value;
71 | };
72 |
73 | my.unique = function(data, key, sort, includeNull){
74 | includeNull = isDef(includeNull) ? includeNull : false;
75 | var values = d3.nest().key(key).map(data, d3.map).keys();
76 | var hasNull = false;
77 | values = values.filter(function(d) {
78 | var isNull = (d == "null");
79 | hasNull = hasNull || isNull;
80 | return !isNull;
81 | }).map(toInt);
82 | if (isDef(sort)) {
83 | values = values.sort(sort);
84 | }
85 | if (includeNull && hasNull) {
86 | values.unshift(null);
87 | }
88 | return values;
89 | };
90 |
91 | function isDef(value) {
92 | return typeof value !== 'undefined';
93 | }
94 |
95 | function mapKey(key) {
96 | return function(d) { return d.hasOwnProperty(key) ? d[key] : null };
97 | }
98 |
99 | function obj2enum(obj) {
100 | var result = { enumAll: [] };
101 | for (var key in obj) {
102 | result[key] = {
103 | key: key,
104 | value: obj[key]
105 | };
106 | result.enumAll.push(result[key]);
107 | }
108 | return result;
109 | };
110 |
111 | function toInt(d) {
112 | return +d;
113 | }
114 |
115 | function createNumberFormat(thousandsSeparator) {
116 | return function(d) {
117 | if (d == null) {
118 | return LABEL_NULL;
119 | } else {
120 | var parts = d.toString().split(".");
121 | parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
122 | return parts.join(".");
123 | }
124 | };
125 | }
126 |
127 | parent[my.name] = my;
128 |
129 | return parent;
130 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/js/ScalaMeter/main.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "main" };
3 |
4 | /*
5 | * ----- public functions -----
6 | */
7 |
8 | my.init = function() {
9 | parent.chart.init();
10 | parent.filter.init();
11 | parent.dimensions.init();
12 | parent.permalink.init();
13 |
14 | load();
15 | };
16 |
17 | function load() {
18 | loadModules([parent.filter, parent.chart], parent.filter.update);
19 | }
20 |
21 | function loadModules(modules, onLoad) {
22 | var nWaiting = modules.length;
23 |
24 | modules.forEach(function(module) {
25 | module.load(function() {
26 | nWaiting--;
27 | if (nWaiting == 0) {
28 | onLoad();
29 | }
30 | });
31 | });
32 | }
33 |
34 | parent[my.name] = my;
35 |
36 | return parent;
37 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/js/ScalaMeter/permalink.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "permalink" };
3 |
4 | var BITLY_ACCESS_TOKEN = "34fda5dc3ef2ea36e6caf295f4a6443b4afa7401";
5 |
6 | var URL_PARAM_NAME = "config";
7 |
8 | var storedData_;
9 |
10 | /*
11 | * ----- public functions -----
12 | */
13 |
14 | my.init = function() {
15 | storedData_ = parseUrl();
16 |
17 | window.onhashchange = function() {
18 | location.reload();
19 | };
20 | // window.onhashchange = function() {
21 | // storedData_ = parseUrl();
22 | // parent.main.reload();
23 | // };
24 |
25 | var permalinkBtn = ".btn-permalink";
26 |
27 | $(permalinkBtn).popover({
28 | placement: 'bottom',
29 | trigger: 'manual',
30 | title: 'Press Ctrl-C to copy Shorten',
31 | html: true
32 | }).click(function(event) {
33 | event.preventDefault();
34 | $(this).data('popover').options.content = '';
35 | $(this).popover('toggle');
36 | longUrl = getPermalinkUrl();
37 | displayUrl(longUrl);
38 | if (isOnLocalhost()) {
39 | $('.permalink-shorten').hide();
40 | } else {
41 | $('.permalink-shorten').click(function(event) {
42 | event.preventDefault();
43 | bitlyShorten(longUrl, function(shortUrl) {
44 | displayUrl(shortUrl);
45 | });
46 | });
47 | }
48 | });
49 |
50 | $(':not(' + permalinkBtn + ')').click(function(event) {
51 | if (!event.isDefaultPrevented()) {
52 | $(permalinkBtn).popover('hide');
53 | }
54 | });
55 | };
56 |
57 | my.storedData = function() {
58 | return storedData_;
59 | };
60 |
61 | /*
62 | * ----- private functions -----
63 | */
64 |
65 | function getConfig() {
66 | return {
67 | filterConfig: parent.filter.getConfig(),
68 | chartConfig: parent.chart.getConfig()
69 | };
70 | }
71 |
72 | function displayUrl(url) {
73 | $('.permalink-inner')
74 | .val(url)
75 | .focus()
76 | .select()
77 | .click(function(event) {
78 | $(this).select();
79 | event.preventDefault();
80 | });
81 | }
82 |
83 | function getPermalinkUrl() {
84 | var data = {};
85 | data[URL_PARAM_NAME] = JSON.stringify(getConfig());
86 | return document.URL.split('#')[0] + "#" + jQuery.param(data);
87 | }
88 |
89 | function getUrlParams() {
90 | var match,
91 | pl = /\+/g,
92 | search = /([^&=]+)=?([^&]*)/g,
93 | decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
94 | query = window.location.hash.substring(1);
95 |
96 | var urlParams = {};
97 | while (match = search.exec(query)) {
98 | urlParams[decode(match[1])] = decode(match[2]);
99 | }
100 | return urlParams;
101 | };
102 |
103 | function bitlyShorten(longUrl, func) {
104 | $.getJSON(
105 | "https://api-ssl.bitly.com/v3/shorten",
106 | {
107 | "access_token": BITLY_ACCESS_TOKEN,
108 | "longUrl": longUrl
109 | },
110 | function(response) {
111 | func(response.data.url);
112 | }
113 | );
114 | }
115 |
116 | function isOnLocalhost() {
117 | var hostname = window.location.hostname;
118 | var protocol = window.location.protocol;
119 | return hostname == "127.0.0.1" || hostname == "localhost" || protocol == "file:";
120 | }
121 |
122 | function parseUrl() {
123 | var data = getUrlParams();
124 | if (data.hasOwnProperty(URL_PARAM_NAME)) {
125 | return $.parseJSON(data[URL_PARAM_NAME]);
126 | } else {
127 | return null;
128 | }
129 | }
130 |
131 | parent[my.name] = my;
132 |
133 | return parent;
134 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/js/jquery-compat.js:
--------------------------------------------------------------------------------
1 | // source: http://stackoverflow.com/questions/9638247/is-jquery-browser-deprecated
2 | // Limit scope pollution from any deprecated API
3 | (function() {
4 |
5 | var matched, browser;
6 |
7 | // Use of jQuery.browser is frowned upon.
8 | // More details: http://api.jquery.com/jQuery.browser
9 | // jQuery.uaMatch maintained for back-compat
10 | jQuery.uaMatch = function( ua ) {
11 | ua = ua.toLowerCase();
12 |
13 | var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
14 | /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
15 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
16 | /(msie) ([\w.]+)/.exec( ua ) ||
17 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
18 | [];
19 |
20 | return {
21 | browser: match[ 1 ] || "",
22 | version: match[ 2 ] || "0"
23 | };
24 | };
25 |
26 | matched = jQuery.uaMatch( navigator.userAgent );
27 | browser = {};
28 |
29 | if ( matched.browser ) {
30 | browser[ matched.browser ] = true;
31 | browser.version = matched.version;
32 | }
33 |
34 | // Chrome is Webkit, but Webkit is also Safari.
35 | if ( browser.chrome ) {
36 | browser.webkit = true;
37 | } else if ( browser.webkit ) {
38 | browser.safari = true;
39 | }
40 |
41 | jQuery.browser = browser;
42 |
43 | jQuery.sub = function() {
44 | function jQuerySub( selector, context ) {
45 | return new jQuerySub.fn.init( selector, context );
46 | }
47 | jQuery.extend( true, jQuerySub, this );
48 | jQuerySub.superclass = this;
49 | jQuerySub.fn = jQuerySub.prototype = this();
50 | jQuerySub.fn.constructor = jQuerySub;
51 | jQuerySub.sub = this.sub;
52 | jQuerySub.fn.init = function init( selector, context ) {
53 | if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
54 | context = jQuerySub( context );
55 | }
56 |
57 | return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
58 | };
59 | jQuerySub.fn.init.prototype = jQuerySub.fn;
60 | var rootjQuerySub = jQuerySub(document);
61 | return jQuerySub;
62 | };
63 |
64 | })();
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/css/bootstrap-slider.css:
--------------------------------------------------------------------------------
1 | .ui-slider .ui-slider-range {
2 | position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0;
3 | color: #ffffff;
4 | background-color: #0064cd;
5 | background-repeat: repeat-x;
6 | background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));
7 | background-image: -moz-linear-gradient(top, #049cdb, #0064cd);
8 | background-image: -ms-linear-gradient(top, #049cdb, #0064cd);
9 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));
10 | background-image: -webkit-linear-gradient(top, #049cdb, #0064cd);
11 | background-image: -o-linear-gradient(top, #049cdb, #0064cd);
12 | background-image: linear-gradient(top, #049cdb, #0064cd);
13 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);
14 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
15 | border-color: #0064cd #0064cd #003f81;
16 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
17 | }
18 | .ui-widget-header {
19 | font-weight:bold;
20 | border-color: #0064cd #0064cd #003f81;
21 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
22 | border:1px solid #666;
23 | }
24 | .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
25 |
26 | background-color: #e6e6e6;
27 | background-repeat: no-repeat;
28 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));
29 | background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
30 | background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);
31 | background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
32 | background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
33 | background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
34 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
35 |
36 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
37 |
38 | color: #333;
39 | /*font-size: 13px;*/
40 | line-height: normal;
41 | border: 1px solid #ccc;
42 | border-bottom-color: #bbb;
43 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
44 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
45 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
46 | -webkit-transition: 0.1s linear background-image;
47 | -moz-transition: 0.1s linear background-image;
48 | -ms-transition: 0.1s linear background-image;
49 | -o-transition: 0.1s linear background-image;
50 | transition: 0.1s linear background-image;
51 | overflow: visible;
52 |
53 | }
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/css/icons.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/report/css/icons.gif
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/css/vline.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/report/css/vline.gif
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/img/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/report/img/arrow.png
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-assets/results/runtime/report/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Performance Report
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
50 |
51 |
52 |
53 |
54 |
55 |
x-axis
56 |
57 |
58 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
97 |
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/js/ScalaMeter/dimensions.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "dimensions" };
3 |
4 | /*
5 | * ----- imports -----
6 | */
7 | var h;
8 |
9 | var keys_,
10 | params_;
11 |
12 | my.init = function() {
13 | h = parent.helper;
14 |
15 | keys_ = [];
16 | params_ = d3.map();
17 | };
18 |
19 | my.addParam = function(key) {
20 | if (!params_.has(key)) {
21 | params_.set(key, addDimension(key, key.substr(h.dKey.paramPrefix.length)));
22 | keys_.push(key);
23 | }
24 | };
25 |
26 | my.filterValues = function(data, legendOrder) {
27 | keys_.forEach(function(key, i) {
28 | var dim = params_.get(key);
29 | dim.filteredValues(h.unique(data, dim.keyFn(), i == 0 ? d3.ascending : legendOrder, true));
30 | });
31 | };
32 |
33 | my.add = function(key) {
34 | var newDim = addDimension(key, key);
35 | params_.set(key, newDim);
36 | keys_.push(key);
37 | return newDim;
38 | }
39 |
40 | my.get = function(key) {
41 | return params_.get(key);
42 | };
43 |
44 | my.getAll = function() {
45 | return keys_.map(function(key) {
46 | return params_.get(key);
47 | });
48 | };
49 |
50 | my.keys = function(_) {
51 | if (!arguments.length) return keys_;
52 | keys_ = _;
53 | };
54 |
55 | function addDimension(key, caption) {
56 | return (function() {
57 | var key_ = key,
58 | caption_ = caption,
59 | selectMode_ = h.selectModes.single,
60 | selectedValues_ = d3.set(),
61 | expanded_ = false,
62 | format_ = h.numberFormat,
63 | keyFn_ = h.mapKey(key),
64 | filterContainer_,
65 | values_,
66 | filteredValues_,
67 | cfDimension_;
68 |
69 | function updateCrossfilter() {
70 | cfDimension_.filterFunction(function(d) {
71 | return d == null || selectedValues_.has(d);
72 | });
73 | }
74 |
75 | function updateSelectMode() {
76 | switch (selectMode_) {
77 | case h.selectModes.single:
78 | var selectedValue = values_[0];
79 | for (var i = 0; i < values_.length; i++) {
80 | if (selectedValues_.has(values_[i])) {
81 | selectedValue = values_[i];
82 | break;
83 | }
84 | }
85 | selectedValues_ = d3.set([selectedValue]);
86 | break;
87 | case h.selectModes.all:
88 | selectedValues_ = d3.set(values_);
89 | break;
90 | }
91 | }
92 |
93 | return {
94 | init: function(data, cfDimension) {
95 | values_ = h.unique(data, h.mapKey(key), d3.ascending);
96 |
97 | // if (key == h.dKey.date) {
98 | // //generate random dates over the past 5 years
99 | // var today = +new Date();
100 | // for(i=0; i<5000; i++){
101 | // values_.push(today - 50 * 86400 * 1000 - Math.round(Math.random() * 5 * 365 * 86400 * 1000));
102 | // }
103 | // values_.sort(d3.ascending);
104 | // }
105 |
106 | selectedValues_ = d3.set(values_);
107 | cfDimension_ = cfDimension;
108 | },
109 |
110 | updateCrossfilter : updateCrossfilter,
111 |
112 | selectMode: function(_) {
113 | if (!arguments.length) return selectMode_;
114 | selectMode_ = _;
115 | updateSelectMode();
116 | },
117 |
118 | selectedValues: function(_) {
119 | if (!arguments.length) return selectedValues_;
120 | selectedValues_ = _;
121 | },
122 |
123 | filteredValues: function(_) {
124 | if (!arguments.length) return filteredValues_;
125 | filteredValues_ = _;
126 | },
127 |
128 | caption: function(_) {
129 | if (!arguments.length) return caption_;
130 | caption_ = _;
131 | },
132 |
133 | format: function(_) {
134 | if (!arguments.length) return format_;
135 | format_ = _;
136 | },
137 |
138 | filterContainer: function(_) {
139 | if (!arguments.length) return filterContainer_;
140 | filterContainer_ = _;
141 | },
142 |
143 | expanded: function(_) {
144 | if (!arguments.length) return expanded_;
145 | expanded_ = _;
146 | },
147 |
148 | key: function() {
149 | return key_;
150 | },
151 |
152 | keyFn: function() {
153 | return keyFn_;
154 | },
155 |
156 | clickValue: function(value) {
157 | if (selectMode_ == h.selectModes.single) {
158 | selectedValues_ = d3.set([value]);
159 | } else {
160 | if (selectedValues_.has(value)) {
161 | selectedValues_.remove(value);
162 | } else {
163 | selectedValues_.add(value);
164 | }
165 | if (selectedValues_.values().length == values_.length) {
166 | selectMode_ = h.selectModes.all;
167 | } else {
168 | selectMode_ = h.selectModes.select;
169 | }
170 | }
171 | },
172 |
173 | getAllValues: function() {
174 | return values_;
175 | },
176 |
177 | selectLast: function() {
178 | selectedValues_ = d3.set([values_[values_.length - 1]]);
179 | }
180 | };
181 | })();
182 | }
183 |
184 | parent[my.name] = my;
185 |
186 | return parent;
187 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/js/ScalaMeter/helper.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "helper" };
3 |
4 | var TSV_DATA_KEYS = {
5 | date: "date",
6 | paramPrefix: "param-",
7 | param: "param-size",
8 | value: "value",
9 | success: "success",
10 | cilo: "cilo",
11 | cihi: "cihi",
12 | complete: "complete",
13 | curve: "curve",
14 | index: "index"
15 | };
16 |
17 | var SELECT_MODES = obj2enum({
18 | single: "Single",
19 | select: "Select",
20 | all: "All"
21 | });
22 |
23 | var LABEL_NULL = "none";
24 |
25 | var DATE_FORMAT = (function(d) {return d3.time.format("%Y-%m-%d %H:%M:%S")(new Date(+d)); });
26 |
27 | var NUMBER_FORMAT = createNumberFormat("\u202F"); // 202F: narrow no-break space
28 |
29 | my.curveKey = mapKey(TSV_DATA_KEYS.curve);
30 |
31 | my.dKey = TSV_DATA_KEYS;
32 |
33 | my.isDef = isDef;
34 |
35 | my.mapKey = mapKey;
36 |
37 | my.selectModes = SELECT_MODES;
38 |
39 | my.dateFormat = DATE_FORMAT;
40 |
41 | my.numberFormat = NUMBER_FORMAT;
42 |
43 | my.mainColors = (function() {
44 | var nGroups = 10;
45 | var groups = d3.scale.category10().domain(d3.range(nGroups));
46 | return function(i) {
47 | return groups(i % nGroups);
48 | };
49 | })();
50 |
51 | my.ident = function(d) {
52 | return d;
53 | };
54 |
55 | my.ascendingToInt = function(a, b) {
56 | return d3.ascending(+a, +b);
57 | };
58 |
59 | my.sortBy = function(fn) {
60 | return function(a, b) {
61 | return d3.ascending(fn(a), fn(b));
62 | };
63 | };
64 |
65 | my.fKey = function(d) {
66 | return d.key;
67 | };
68 |
69 | my.fValue = function(d) {
70 | return d.value;
71 | };
72 |
73 | my.unique = function(data, key, sort, includeNull){
74 | includeNull = isDef(includeNull) ? includeNull : false;
75 | var values = d3.nest().key(key).map(data, d3.map).keys();
76 | var hasNull = false;
77 | values = values.filter(function(d) {
78 | var isNull = (d == "null");
79 | hasNull = hasNull || isNull;
80 | return !isNull;
81 | }).map(toInt);
82 | if (isDef(sort)) {
83 | values = values.sort(sort);
84 | }
85 | if (includeNull && hasNull) {
86 | values.unshift(null);
87 | }
88 | return values;
89 | };
90 |
91 | function isDef(value) {
92 | return typeof value !== 'undefined';
93 | }
94 |
95 | function mapKey(key) {
96 | return function(d) { return d.hasOwnProperty(key) ? d[key] : null };
97 | }
98 |
99 | function obj2enum(obj) {
100 | var result = { enumAll: [] };
101 | for (var key in obj) {
102 | result[key] = {
103 | key: key,
104 | value: obj[key]
105 | };
106 | result.enumAll.push(result[key]);
107 | }
108 | return result;
109 | };
110 |
111 | function toInt(d) {
112 | return +d;
113 | }
114 |
115 | function createNumberFormat(thousandsSeparator) {
116 | return function(d) {
117 | if (d == null) {
118 | return LABEL_NULL;
119 | } else {
120 | var parts = d.toString().split(".");
121 | parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
122 | return parts.join(".");
123 | }
124 | };
125 | }
126 |
127 | parent[my.name] = my;
128 |
129 | return parent;
130 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/js/ScalaMeter/main.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "main" };
3 |
4 | /*
5 | * ----- public functions -----
6 | */
7 |
8 | my.init = function() {
9 | parent.chart.init();
10 | parent.filter.init();
11 | parent.dimensions.init();
12 | parent.permalink.init();
13 |
14 | load();
15 | };
16 |
17 | function load() {
18 | loadModules([parent.filter, parent.chart], parent.filter.update);
19 | }
20 |
21 | function loadModules(modules, onLoad) {
22 | var nWaiting = modules.length;
23 |
24 | modules.forEach(function(module) {
25 | module.load(function() {
26 | nWaiting--;
27 | if (nWaiting == 0) {
28 | onLoad();
29 | }
30 | });
31 | });
32 | }
33 |
34 | parent[my.name] = my;
35 |
36 | return parent;
37 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/js/ScalaMeter/permalink.js:
--------------------------------------------------------------------------------
1 | var ScalaMeter = (function(parent) {
2 | var my = { name: "permalink" };
3 |
4 | var BITLY_ACCESS_TOKEN = "34fda5dc3ef2ea36e6caf295f4a6443b4afa7401";
5 |
6 | var URL_PARAM_NAME = "config";
7 |
8 | var storedData_;
9 |
10 | /*
11 | * ----- public functions -----
12 | */
13 |
14 | my.init = function() {
15 | storedData_ = parseUrl();
16 |
17 | window.onhashchange = function() {
18 | location.reload();
19 | };
20 | // window.onhashchange = function() {
21 | // storedData_ = parseUrl();
22 | // parent.main.reload();
23 | // };
24 |
25 | var permalinkBtn = ".btn-permalink";
26 |
27 | $(permalinkBtn).popover({
28 | placement: 'bottom',
29 | trigger: 'manual',
30 | title: 'Press Ctrl-C to copy Shorten',
31 | html: true
32 | }).click(function(event) {
33 | event.preventDefault();
34 | $(this).data('popover').options.content = '';
35 | $(this).popover('toggle');
36 | longUrl = getPermalinkUrl();
37 | displayUrl(longUrl);
38 | if (isOnLocalhost()) {
39 | $('.permalink-shorten').hide();
40 | } else {
41 | $('.permalink-shorten').click(function(event) {
42 | event.preventDefault();
43 | bitlyShorten(longUrl, function(shortUrl) {
44 | displayUrl(shortUrl);
45 | });
46 | });
47 | }
48 | });
49 |
50 | $(':not(' + permalinkBtn + ')').click(function(event) {
51 | if (!event.isDefaultPrevented()) {
52 | $(permalinkBtn).popover('hide');
53 | }
54 | });
55 | };
56 |
57 | my.storedData = function() {
58 | return storedData_;
59 | };
60 |
61 | /*
62 | * ----- private functions -----
63 | */
64 |
65 | function getConfig() {
66 | return {
67 | filterConfig: parent.filter.getConfig(),
68 | chartConfig: parent.chart.getConfig()
69 | };
70 | }
71 |
72 | function displayUrl(url) {
73 | $('.permalink-inner')
74 | .val(url)
75 | .focus()
76 | .select()
77 | .click(function(event) {
78 | $(this).select();
79 | event.preventDefault();
80 | });
81 | }
82 |
83 | function getPermalinkUrl() {
84 | var data = {};
85 | data[URL_PARAM_NAME] = JSON.stringify(getConfig());
86 | return document.URL.split('#')[0] + "#" + jQuery.param(data);
87 | }
88 |
89 | function getUrlParams() {
90 | var match,
91 | pl = /\+/g,
92 | search = /([^&=]+)=?([^&]*)/g,
93 | decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
94 | query = window.location.hash.substring(1);
95 |
96 | var urlParams = {};
97 | while (match = search.exec(query)) {
98 | urlParams[decode(match[1])] = decode(match[2]);
99 | }
100 | return urlParams;
101 | };
102 |
103 | function bitlyShorten(longUrl, func) {
104 | $.getJSON(
105 | "https://api-ssl.bitly.com/v3/shorten",
106 | {
107 | "access_token": BITLY_ACCESS_TOKEN,
108 | "longUrl": longUrl
109 | },
110 | function(response) {
111 | func(response.data.url);
112 | }
113 | );
114 | }
115 |
116 | function isOnLocalhost() {
117 | var hostname = window.location.hostname;
118 | var protocol = window.location.protocol;
119 | return hostname == "127.0.0.1" || hostname == "localhost" || protocol == "file:";
120 | }
121 |
122 | function parseUrl() {
123 | var data = getUrlParams();
124 | if (data.hasOwnProperty(URL_PARAM_NAME)) {
125 | return $.parseJSON(data[URL_PARAM_NAME]);
126 | } else {
127 | return null;
128 | }
129 | }
130 |
131 | parent[my.name] = my;
132 |
133 | return parent;
134 | })(ScalaMeter || {});
--------------------------------------------------------------------------------
/caustic-assets/results/runtime/report/js/jquery-compat.js:
--------------------------------------------------------------------------------
1 | // source: http://stackoverflow.com/questions/9638247/is-jquery-browser-deprecated
2 | // Limit scope pollution from any deprecated API
3 | (function() {
4 |
5 | var matched, browser;
6 |
7 | // Use of jQuery.browser is frowned upon.
8 | // More details: http://api.jquery.com/jQuery.browser
9 | // jQuery.uaMatch maintained for back-compat
10 | jQuery.uaMatch = function( ua ) {
11 | ua = ua.toLowerCase();
12 |
13 | var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
14 | /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
15 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
16 | /(msie) ([\w.]+)/.exec( ua ) ||
17 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
18 | [];
19 |
20 | return {
21 | browser: match[ 1 ] || "",
22 | version: match[ 2 ] || "0"
23 | };
24 | };
25 |
26 | matched = jQuery.uaMatch( navigator.userAgent );
27 | browser = {};
28 |
29 | if ( matched.browser ) {
30 | browser[ matched.browser ] = true;
31 | browser.version = matched.version;
32 | }
33 |
34 | // Chrome is Webkit, but Webkit is also Safari.
35 | if ( browser.chrome ) {
36 | browser.webkit = true;
37 | } else if ( browser.webkit ) {
38 | browser.safari = true;
39 | }
40 |
41 | jQuery.browser = browser;
42 |
43 | jQuery.sub = function() {
44 | function jQuerySub( selector, context ) {
45 | return new jQuerySub.fn.init( selector, context );
46 | }
47 | jQuery.extend( true, jQuerySub, this );
48 | jQuerySub.superclass = this;
49 | jQuerySub.fn = jQuerySub.prototype = this();
50 | jQuerySub.fn.constructor = jQuerySub;
51 | jQuerySub.sub = this.sub;
52 | jQuerySub.fn.init = function init( selector, context ) {
53 | if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
54 | context = jQuerySub( context );
55 | }
56 |
57 | return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
58 | };
59 | jQuerySub.fn.init.prototype = jQuerySub.fn;
60 | var rootjQuerySub = jQuerySub(document);
61 | return jQuerySub;
62 | };
63 |
64 | })();
--------------------------------------------------------------------------------
/caustic-benchmark/README.md:
--------------------------------------------------------------------------------
1 | # Benchmark
2 | Performance benchmarks were conducted on a t2.xlarge instance with 32GB of RAM and 8 CPUs and
3 | profiling was conducting using [JProfiler][1]. When running the benchmarking suite the JVM heap
4 | size must be manually configured to avoid triggering the garbage collector or an
5 | ```OutOfMemoryError```. This can be done in the following manner. We recommend setting the
6 | minimum heap size to at least 2 GB.
7 |
8 | ```bash
9 | ./pants run caustic-benchmark/src/main/scala:bin \
10 | --jvm-run-jvm-options='-Xms2048M -Xmx4096M' \ // JVM heap size.
11 | -- throughput // Name of benchmark.
12 | ```
13 |
14 | ## Latency
15 | ![Latency][2]
16 |
17 | In this benchmark, we generate programs consisting of the specified number of read expressions and
18 | measure the time it takes for the program to commit. We see that latency grows linearly with program
19 | size. In other words, *each additional expression has a constant overhead*.
20 |
21 | ## Throughput
22 | ![Throughput][3]
23 |
24 | In this benchmark, the specified number of threads simultaneously execute a series of programs that
25 | each read and write the specified percentages of the key-space. Note that the larger the percentages
26 | of reads and writes and the greater the number of threads, the greater the likelihood that
27 | transactions will fail and the more significant the reduction in throughput will be. We see that in
28 | read-only workloads, throughput scales linearly with the number of threads. As the percentage of
29 | reads and writes and the number of threads increases, throughput falls considerably but still scales
30 | linearly with the number of threads.
31 |
32 | [1]: https://www.ej-technologies.com/products/jprofiler/overview.html
33 | [2]: https://raw.githubusercontent.com/ashwin153/caustic/master/caustic-assets/images/runtime-latency.png
34 | [3]: https://raw.githubusercontent.com/ashwin153/caustic/master/caustic-assets/images/runtime-throughput.png
35 |
--------------------------------------------------------------------------------
/caustic-benchmark/src/main/scala/BUILD:
--------------------------------------------------------------------------------
1 | scala_library(
2 | name='scala',
3 | sources=rglobs('*.scala'),
4 | dependencies=[
5 | 'caustic-runtime/src/main/scala',
6 | '3rdparty/jvm:scalameter',
7 | ],
8 | )
9 |
10 | jvm_binary(
11 | name='bin',
12 | basename='caustic-benchmark',
13 | source='caustic/benchmark/Suite.scala',
14 | main='caustic.benchmark.Suite',
15 | dependencies=[
16 | 'caustic-benchmark/src/main/scala',
17 | ],
18 | )
19 |
20 |
21 |
--------------------------------------------------------------------------------
/caustic-benchmark/src/main/scala/caustic/benchmark/Suite.scala:
--------------------------------------------------------------------------------
1 | package caustic.benchmark
2 |
3 | import caustic.benchmark.runtime._
4 |
5 | /**
6 | * A benchmarking suite.
7 | */
8 | object Suite extends App {
9 |
10 | // Execute the specified benchmark.
11 | this.args(0) match {
12 | case "latency" => LatencyBenchmark.main(args.drop(1))
13 | case "throughput" => ThroughputBenchmark.main(args.drop(1))
14 | case "volume" => VolumeBenchmark.main(args.drop(1))
15 | case "profile" => ProfileBenchmark.main(args.drop(1))
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/caustic-benchmark/src/main/scala/caustic/benchmark/runtime/LatencyBenchmark.scala:
--------------------------------------------------------------------------------
1 | package caustic.benchmark.runtime
2 |
3 | import caustic.runtime._
4 |
5 | import org.scalameter.Bench
6 | import org.scalameter.api._
7 |
8 | /**
9 | * Benchmarks the latency of the runtime. Results show that the runtime scales linearly with
10 | * transaction size. We show that Caustic adds tens of milliseconds to the runtime of transactions
11 | * that span thousands of keys.
12 | */
13 | object LatencyBenchmark extends Bench.ForkedTime {
14 |
15 | // Construct an in-memory runtime.
16 | val runtime: Runtime = Runtime(Volume.Memory())
17 |
18 | // Benchmark reads.
19 | val reads: Gen[Program] = Gen
20 | .exponential("size")(2, 1 << 12, 2)
21 | .map(size => Seq.tabulate(size)(i => read(s"x$i")).reduce(cons))
22 |
23 | // Benchmark writes.
24 | val writes: Gen[Program] = Gen
25 | .exponential("size")(2, 1 << 12, 2)
26 | .map(size => Seq.tabulate(size)(i => write(s"x$i", i)).reduce(cons))
27 |
28 | // Benchmark writes then reads.
29 | val readAfterWrites: Gen[Program] = Gen
30 | .exponential("size")(2, 1 << 12, 2)
31 | .map(_ / 2)
32 | .map(size => Seq.tabulate(size)(i => cons(write(s"x$i", i), read(s"x$i"))).reduce(cons))
33 |
34 | performance of "Runtime" in {
35 | measure method "Runtime.execute" in {
36 | using(this.reads) curve "Read Latency" in { this.runtime.execute }
37 | using(this.writes) curve "Write Latency" in { this.runtime.execute }
38 | using(this.readAfterWrites) curve "Read-After-Write Latency" in { this.runtime.execute }
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/caustic-benchmark/src/main/scala/caustic/benchmark/runtime/ProfileBenchmark.scala:
--------------------------------------------------------------------------------
1 | package caustic.benchmark.runtime
2 |
3 | import caustic.runtime._
4 |
5 | /**
6 | * Benchmarks the CPU and memory footprint of the runtime. To run this benchmark, install JProfiler
7 | * and acquire a license key from EJ Technologies. Then, install the JProfiler IntelliJ plugin.
8 | * Optionally enable allocation monitoring for additional visibility into the system.
9 | */
10 | object ProfileBenchmark extends App {
11 |
12 | // Construct a program and execute it on a runtime.
13 | val runtime = Runtime(Volume.Memory())
14 | val program = Seq.tabulate(4096)(i => write(s"x$i", i)).reduce(cons)
15 | this.runtime.execute(this.program)
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/caustic-benchmark/src/main/scala/caustic/benchmark/runtime/ThroughputBenchmark.scala:
--------------------------------------------------------------------------------
1 | package caustic.benchmark.runtime
2 |
3 | import caustic.runtime._
4 |
5 | import scala.util.Random
6 |
7 | /**
8 | * Benchmarks the throughput of the runtime under varying workloads. Adjusting the size of the key
9 | * space and the relative proportion of keys that are read and written in each transaction
10 | * implicitly changes the contention probability, the likelihood that any two concurrent
11 | * transactions conflict.
12 | */
13 | object ThroughputBenchmark extends App {
14 |
15 | val keys = 1000 // Total number of keys.
16 | val reads = 0.01 // Percentage of keys read in each program.
17 | val writes = 0.00 // Percentage of keys written in each program.
18 | val attempts = 10000 // Number of attempts per thread.
19 | val runtime = Runtime(Volume.Memory()) // In-memory runtime.
20 |
21 | (1 to 8) foreach { threads =>
22 | println {
23 | // Construct the specified number of threads and concurrent generate and execute programs.
24 | val program = Seq.fill(threads)(Seq.fill(attempts)(gen))
25 | val current = System.nanoTime()
26 | val success = program.par.map(_.map(runtime.execute).count(_.isSuccess)).sum
27 | val elapsed = System.nanoTime() - current
28 | 1E9 * success / elapsed
29 | }
30 | }
31 |
32 | /**
33 | * Generates a randomized transaction that performs the specified number of reads and writes on
34 | * a key space of the specified size.
35 | *
36 | * @return Randomly generated transaction.
37 | */
38 | def gen: Program =
39 | (random(keys, reads).map(read) ++ random(keys, writes).map(write(_, real(1)))).reduce(cons)
40 |
41 | /**
42 | * Returns a sequence of l integers drawn uniformly at random from [0, n).
43 | *
44 | * @param n Population size.
45 | * @param l Sample size.
46 | * @return Uniformly random integers.
47 | */
48 | def random(n: Int, l: Double): Seq[Program] =
49 | Random.shuffle(Seq.range(0, n)).take((n * l).toInt).map(x => text(x.toString))
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/caustic-benchmark/src/main/scala/caustic/benchmark/runtime/VolumeBenchmark.scala:
--------------------------------------------------------------------------------
1 | package caustic.benchmark.runtime
2 |
3 | import beaker.client._
4 | import caustic.runtime._
5 |
6 | import org.scalameter.Bench
7 | import org.scalameter.api.Gen
8 |
9 | /**
10 | * Benchmarks the get and cas performance of the underlying in-memory volume used in other
11 | * benchmarks. Results show that gets succeed in approximately 6 microseconds even for large
12 | * transactions. However, cas is several orders of magnitude slower with a cost linear in the size
13 | * of the transaction. Cas costs around 1 millisecond for small operations and as much as 5
14 | * milliseconds for larger transactions.
15 | */
16 | object VolumeBenchmark extends Bench.ForkedTime {
17 |
18 | // Construct an in-memory volume.
19 | val volume: Volume = Volume.Memory()
20 |
21 | // Benchmark get.
22 | val get: Gen[Set[String]] = Gen
23 | .exponential("size")(2, 1 << 12, 2)
24 | .map(size => Seq.tabulate(size)(i => s"x$i").toSet)
25 |
26 | // Benchmark cas.
27 | val cas: Gen[(Map[Key, Version], Map[Key, Value])] = get
28 | .map(keys => (keys.map(_ -> 0L).toMap, keys.map(_ -> "asdf").toMap))
29 |
30 | performance of "Volume.Memory" in {
31 | measure method "Get" in {
32 | using(this.get) curve "Get Latency" in { this.volume.get }
33 | }
34 |
35 | measure method "Cas" in {
36 | using(this.cas) curve "Cas Latency" in { case (d, c) => this.volume.cas(d, c) }
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/caustic-compiler/README.md:
--------------------------------------------------------------------------------
1 | # Compiler
2 | The standard library provides additional functionality that is absent in the runtime to make it
3 | easier to construct programs. However, the standard library does not address the syntactic
4 | challenges of expressing programs. At times the standard library is forced to use unintuitive
5 | operators like ```:=``` and ```<>``` and verbose declarations like ```Variable.Remote("x")```,
6 | because of the limitations of implementing a language within a language.
7 |
8 | The compiler translates programs written in the statically-typed, object-oriented Caustic
9 | programming language into operations on the standard library. The Caustic language is to the runtime
10 | as C is to Assembly. While runtime programs are powerful, they lack the expressivity of most modern
11 | programming languages. The Caustic compiler, or ```Causticc```, translates code written in Caustic
12 | into runtime-compatible programs, to provide both the robustness of a general-purpose programming
13 | language and the transactional guarantees of the runtime. For example, consider the following
14 | example of a distributed counter written in Caustic. This program may be run without modification
15 | on any underlying volume and distributed arbitrarily without error. The [Akka][1] project also
16 | provides a similar [implementation][2] of a backend-agnostic distributed counter. Their
17 | implementation is almost seven times longer.
18 |
19 | ```
20 | module caustic.example
21 |
22 | /**
23 | * A count.
24 | *
25 | * @param value Current value.
26 | */
27 | struct Total {
28 | value: Int
29 | }
30 |
31 | /**
32 | * A distributed counter.
33 | */
34 | service Counter {
35 |
36 | /**
37 | * Increments the total and returns its current value.
38 | *
39 | * @param x Reference to total.
40 | * @return Current value.
41 | */
42 | def increment(x: Total&): Int = {
43 | if (x.value) x.value += 1 else x.value = 1
44 | x.value
45 | }
46 |
47 | }
48 | ```
49 |
50 | ## Implementation
51 | The compiler uses [ANTLR][3] to generate a predicated LL(*) parser from an ANTLR [grammar file][4].
52 | It applies this parser to source files and walks the resulting parse-tree to generate code
53 | compatible with the standard library. Most statements in Caustic have direct equivalents in the
54 | standard library, and, for the most part, the compiler acts as a kind of intelligent
55 | find-and-replace. However, there are certain aspects of the compiler that are non-trivial.
56 |
57 | First, the compiler performs type inference. It is able statically verify types and method
58 | signatures by maintaining a lexically-scoped __universe__ of the various variables, records,
59 | and functions that have been defined. Because the type system is relatively simplistic, static
60 | types almost never need to be specified. The combination of a static type system and aggressive
61 | type inference allows programs to be both type-safe and concise.
62 |
63 | Second, the compiler is directly integrated in the [Pants][5] build system. Pants is an open-source,
64 | cross-language build system. Integration into Pants means that Caustic programs are interoperable
65 | with a variety of existing languages and tooling.
66 |
67 | Third, the compiler provides a [TextMate][6] bundle that implements syntax highlighting and code
68 | completion for most text editors and IDEs to make it easier for programmers to use the language.
69 |
70 | [1]: https://akka.io/
71 | [2]: https://git.io/vxS6u
72 | [3]: http://www.antlr.org/
73 | [4]: https://github.com/ashwin153/caustic/blob/master/caustic-compiler/src/main/antlr/Caustic.g4
74 | [5]: https://www.pantsbuild.org/
75 | [6]: https://macromates.com/
--------------------------------------------------------------------------------
/caustic-compiler/src/main/antlr/BUILD:
--------------------------------------------------------------------------------
1 | java_antlr_library(
2 | name='antlr',
3 | sources=['Caustic.g4'],
4 | compiler='antlr4',
5 | package='caustic.grammar',
6 | provides=artifact(
7 | org='com.madavan',
8 | name='caustic-grammar',
9 | repo=public,
10 | publication_metadata=describe,
11 | ),
12 | )
13 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/BUILD:
--------------------------------------------------------------------------------
1 | python_library(
2 | name='python',
3 | sources=rglobs('*.py'),
4 | provides=setup_py(
5 | name='caustic.pants',
6 | version='1.0.7',
7 | description='Caustic plugin for Pants',
8 | url='https://github.com/ashwin153/caustic',
9 | author='Ashwin Madavan',
10 | author_email='ashwin.madavan@gmail.com',
11 | license='Apache License, Version 2.0',
12 | zip_safe=True,
13 | classifiers=[
14 | 'Intended Audience :: Developers',
15 | 'License :: OSI Approved :: Apache Software License',
16 | 'Operating System :: OS Independent',
17 | 'Programming Language :: Python',
18 | ]
19 | )
20 | )
21 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/caustic/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-compiler/src/main/python/caustic/__init__.py
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/caustic/pants/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-compiler/src/main/python/caustic/pants/__init__.py
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/caustic/pants/register.py:
--------------------------------------------------------------------------------
1 | from pants.build_graph.build_file_aliases import BuildFileAliases
2 | from pants.goal.task_registrar import TaskRegistrar as task
3 |
4 | from caustic.pants.targets.caustic_library import CausticLibrary
5 | from caustic.pants.tasks.caustic_gen import CausticGen
6 |
7 |
8 | def build_file_aliases():
9 | return BuildFileAliases(
10 | targets={
11 | 'caustic_library': CausticLibrary,
12 | },
13 | )
14 |
15 |
16 | def register_goals():
17 | task(name='causticc', action=CausticGen).install('gen')
18 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/caustic/pants/targets/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-compiler/src/main/python/caustic/pants/targets/__init__.py
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/caustic/pants/targets/caustic_library.py:
--------------------------------------------------------------------------------
1 | from pants.backend.jvm.targets.jvm_target import JvmTarget
2 |
3 |
4 | class CausticLibrary(JvmTarget):
5 | """
6 | A Caustic library generated from Caustic source files.
7 | """
8 |
9 | def __init__(self, **kwargs):
10 | super(CausticLibrary, self).__init__(**kwargs)
11 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/caustic/pants/tasks/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ashwin153/caustic/d640c71a7e3f537bbe4f81062a187ef7e86997a3/caustic-compiler/src/main/python/caustic/pants/tasks/__init__.py
--------------------------------------------------------------------------------
/caustic-compiler/src/main/python/caustic/pants/tasks/caustic_gen.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | import subprocess
3 |
4 | from pants.backend.jvm.tasks.nailgun_task import NailgunTask
5 | from pants.base.exceptions import TaskError
6 | from pants.base.workunit import WorkUnitLabel
7 | from pants.java.jar.jar_dependency import JarDependency
8 | from pants.task.simple_codegen_task import SimpleCodegenTask
9 |
10 | from caustic.pants.targets.caustic_library import CausticLibrary
11 |
12 |
13 | class CausticGen(SimpleCodegenTask, NailgunTask):
14 | """
15 | Generates Scala source code from Caustic *.acid files.
16 | """
17 |
18 | @classmethod
19 | def register_options(cls, register):
20 | super(CausticGen, cls).register_options(register)
21 | cls.register_jvm_tool(register, 'causticc', classpath_spec='//:causticc', classpath=[
22 | JarDependency(org='com.madavan', name='caustic-compiler_2.12', rev='2.0.3')
23 | ])
24 |
25 | def is_gentarget(self, target):
26 | return isinstance(target, CausticLibrary)
27 |
28 | def synthetic_target_type(self, target):
29 | return CausticLibrary
30 |
31 | def synthetic_target_extra_dependencies(self, target, target_workdir):
32 | return self.resolve_deps([
33 | 'caustic-library/src/main/scala',
34 | 'caustic-runtime/src/main/scala',
35 | '3rdparty/jvm:spray-json',
36 | ])
37 |
38 | def execute_codegen(self, target, target_workdir):
39 | # Generate Scala code using the Caustic compiler.
40 | sources = target.target_base
41 | main = "caustic.compiler.Causticc"
42 | classpath = self.tool_classpath('causticc')
43 | result = self.runjava(classpath=classpath, main=main, args=sources, workunit_name='compile')
44 |
45 | if result != 0:
46 | raise TaskError('Causticc ... exited non-zero ({})'.format(result))
47 |
48 | # Move all the generated sources to the target_workdir.
49 | for src in target.sources_relative_to_buildroot():
50 | gen = src.replace('.acid', '.scala')
51 | shutil.move(gen, target_workdir)
52 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/BUILD:
--------------------------------------------------------------------------------
1 | scala_library(
2 | name='scala',
3 | sources=rglobs('*.scala'),
4 | dependencies=[
5 | 'caustic-compiler/src/main/antlr',
6 | ],
7 | provides=artifact(
8 | org='com.madavan',
9 | name='caustic-compiler_2.12',
10 | repo=public,
11 | publication_metadata=describe,
12 | ),
13 | )
14 |
15 | jvm_binary(
16 | name='bin',
17 | basename='caustic-compiler',
18 | source='caustic/compiler/Causticc.scala',
19 | main='caustic.compiler.Causticc',
20 | dependencies=[
21 | 'caustic-compiler/src/main/scala',
22 | ],
23 | )
24 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/Causticc.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler
2 |
3 | import caustic.compiler.error._
4 | import caustic.compiler.gen._
5 | import caustic.compiler.reflect._
6 | import caustic.grammar.{CausticLexer, CausticParser}
7 | import org.antlr.v4.runtime._
8 | import org.antlr.v4.runtime.misc.Interval
9 | import java.io.FileOutputStream
10 | import java.nio.file.{Files, Path, Paths}
11 | import scala.util.{Failure, Try}
12 | import scala.collection.mutable
13 |
14 | /**
15 | * A compiler for the Caustic programming language.
16 | */
17 | object Causticc {
18 |
19 | def main(args: Array[String]): Unit = {
20 | if (args.length < 1 || args.length > 2) {
21 | println("Usage: causticc ")
22 | System.exit(1)
23 | }
24 |
25 | Files.walk(Paths.get(args(0))).filter(_.toString.endsWith(".acid")) forEach { path =>
26 | val output = new FileOutputStream(path.toString.replaceFirst(".acid$", ".scala"))
27 | output.write(Causticc(path).get.getBytes("UTF-8"))
28 | output.close()
29 | }
30 | }
31 |
32 | /**
33 | * Compiles the specified source stream and returns the generated sources.
34 | *
35 | * @param source Source stream.
36 | * @return Generated sources.
37 | */
38 | def apply(source: CharStream): Try[String] = {
39 | // Lex and parse the program.
40 | val lexer = new CausticLexer(source)
41 | val tokens = new CommonTokenStream(lexer)
42 | val parser = new CausticParser(tokens)
43 |
44 | // Register the custom error handler.
45 | val handler = Handler(
46 | source.getSourceName,
47 | source.getText(Interval.of(0, source.size())) split "\n"
48 | )
49 |
50 | parser.removeErrorListeners()
51 | parser.addErrorListener(handler)
52 |
53 | Try {
54 | // Run code generation and return the result.
55 | val gen = Gen(Universe.root)
56 | val out = gen.visitProgram(parser.program())
57 | out
58 | } recoverWith { case e: Error =>
59 | // Report any errors during code generation.
60 | handler.report(e)
61 | Failure(e)
62 | }
63 | }
64 |
65 | /**
66 | * Compiles the specified source code and returns the generated sources.
67 | *
68 | * @param source Source code.
69 | * @return Generated sources.
70 | */
71 | def apply(source: String): Try[String] = Causticc(CharStreams.fromString(source))
72 |
73 | /**
74 | * Compiles the specified source file and returns the generated sources. Compilation is memoized;
75 | * therefore, the same path will never be recompiled twice, even if it is imported in multiple
76 | * different files. Incremental compilation can be implemented by persisting this information
77 | * across compiler invocations. https://stackoverflow.com/a/36960228/1447029
78 | */
79 | lazy val apply: Path => Try[String] = new mutable.HashMap[Path, Try[String]]() {
80 | override def apply(path: Path): Try[String] = getOrElseUpdate(path, {
81 | val source = if (path.isAbsolute) path else Paths.get("").toAbsolutePath.resolve(path)
82 | Causticc(CharStreams.fromPath(source))
83 | })
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/error/Error.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.error
2 |
3 | import org.antlr.v4.runtime.misc.ParseCancellationException
4 | import org.antlr.v4.runtime.ParserRuleContext
5 |
6 | /**
7 | * A compiler exception.
8 | */
9 | sealed trait Error extends ParseCancellationException {
10 |
11 | /**
12 | * Returns the unique error code associated with the exception.
13 | *
14 | * @return Error code.
15 | */
16 | def code: Int
17 |
18 | /**
19 | * Returns the title of the exception.
20 | *
21 | * @return Title name.
22 | */
23 | def title: String
24 |
25 | /**
26 | * Returns a detailed description of the error.
27 | *
28 | * @return Detailed summary.
29 | */
30 | def description: String
31 |
32 | /**
33 | * Returns the location of the error in the source file.
34 | *
35 | * @return Error location.
36 | */
37 | def trace: Trace
38 |
39 | }
40 |
41 | object Error {
42 |
43 | /**
44 | * An error indicated unknown failure.
45 | *
46 | * @param description Detailed summary.
47 | * @param trace Source location.
48 | */
49 | case class Unknown(description: String, trace: Trace) extends Error {
50 | override val code: Int = 0
51 | override val title: String = "Unknown Error"
52 | }
53 |
54 | /**
55 | * An error indicated the program is syntactically invalid.
56 | *
57 | * @param description Detailed summary.
58 | * @param trace Source location.
59 | */
60 | case class Syntax(description: String, trace: Trace) extends Error {
61 | override val code: Int = 1
62 | override val title: String = "Syntax Error"
63 | }
64 |
65 | /**
66 | * An error indicating that a type does not exist or does not match its expected value.
67 | *
68 | * @param description Detailed summary.
69 | * @param trace Source location.
70 | */
71 | case class Type(description: String, trace: Trace) extends Error {
72 | override val code: Int = 2
73 | override val title: String = "Type Error"
74 | }
75 |
76 | /**
77 | * An error indicating that the program could not be parsed.
78 | *
79 | * @param context ANTLR parsing context.
80 | */
81 | case class Parse(context: ParserRuleContext) extends Error {
82 | override val code: Int = 3
83 | override val title: String = "Parse Error"
84 | override val description: String = s"Unable to parse ${ context.getText }"
85 | override val trace: Trace = Trace(context)
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/error/Handler.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.error
2 |
3 | import org.antlr.v4.runtime._
4 |
5 | import java.io.PrintStream
6 | import scala.Console._
7 |
8 | /**
9 | * An exception handler.
10 | *
11 | * @param file Source file.
12 | * @param source Source code.
13 | * @param output Output stream.
14 | */
15 | case class Handler(
16 | file: String,
17 | source: Seq[String],
18 | output: PrintStream = System.out
19 | ) extends BaseErrorListener {
20 |
21 | /**
22 | * Prints the specified error to the output stream. Error messages are formatted for display in a
23 | * 100 character bash shell. Stylistically inspired by the Dotty compiler.
24 | *
25 | * @see https://scala-lang.org/blog/2016/10/14/dotty-errors.html
26 | * @param error Detected Error.
27 | */
28 | def report(error: Error): Unit = this.output.println {
29 | s"""$RED[E${ "%03d".format(error.code) }] ${ error.title }: $file $RESET
30 | |$CYAN${ "-" * 100 }$RESET
31 | |$CYAN ${ "%4d".format(error.trace.line) } |$RESET ${ this.source(error.trace.line - 1) }
32 | |$CYAN |$RESET ${ " " * error.trace.columns.start }$CYAN${ "^" * error.trace.columns.size }$RESET
33 | |$CYAN |$RESET ${ error.description.grouped(91).mkString(s"\n| $CYAN|$RESET ") }
34 | """.stripMargin
35 | }
36 |
37 | override def syntaxError(
38 | recognizer: Recognizer[_, _],
39 | offendingSymbol: Any,
40 | line: Int,
41 | position: Int,
42 | message: String,
43 | exception: RecognitionException
44 | ): Unit = {
45 | val trace = offendingSymbol match {
46 | case token: Token => Trace(token)
47 | case _ => Trace(line, position to position + 1)
48 | }
49 |
50 | exception match {
51 | case null => report(Error.Unknown(message, trace))
52 | case _ => report(Error.Syntax(message, trace))
53 | }
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/error/Trace.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.error
2 |
3 | import org.antlr.v4.runtime.{ParserRuleContext, Token}
4 |
5 | /**
6 | * A source location.
7 | *
8 | * @param line Line number.
9 | * @param columns Range of offending columns.
10 | */
11 | case class Trace(line: Int, columns: Range)
12 |
13 | object Trace {
14 |
15 | /**
16 | * Constructs a trace from the specified ANTLR token.
17 | *
18 | * @param token ANTLR token.
19 | * @return Source location.
20 | */
21 | def apply(token: Token): Trace = {
22 | val length = token.getStopIndex - token.getStartIndex
23 | val initial = token.getCharPositionInLine
24 | Trace(token.getLine, initial to initial + length)
25 | }
26 |
27 | /**
28 | * Constructs a trace from the specified ANTLR rule.
29 | *
30 | * @param rule ANTLR rule.
31 | * @return Source location.
32 | */
33 | def apply(rule: ParserRuleContext): Trace = {
34 | if (rule.start.getLine != rule.stop.getLine) {
35 | Trace(rule.stop)
36 | } else {
37 | val length = rule.stop.getStopIndex - rule.start.getStartIndex
38 | val initial = rule.start.getCharPositionInLine
39 | Trace(rule.start.getLine, initial to initial + length)
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/gen/Gen.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.gen
2 |
3 | import caustic.compiler.reflect._
4 | import caustic.grammar._
5 |
6 | import scala.collection.JavaConverters._
7 |
8 | /**
9 | * Generates a Scala program from a program.
10 | *
11 | * @param universe Type universe.
12 | */
13 | case class Gen(universe: Universe) extends CausticBaseVisitor[String] {
14 |
15 | override def visitProgram(ctx: CausticParser.ProgramContext): String = {
16 | s"""${ if (ctx.module() != null) s"package ${ ctx.module().Identifier().asScala.mkString(".") }" else "" }
17 | |
18 | |import caustic.library._
19 | |import caustic.library.control._
20 | |import caustic.library.typing._
21 | |import caustic.library.typing.collection._
22 | |import caustic.library.typing.record._
23 | |import caustic.library.typing.Value._
24 | |import caustic.runtime._
25 | |
26 | |import spray.json._
27 | |import DefaultJsonProtocol._
28 | |
29 | |import scala.language.implicitConversions
30 | |import scala.language.reflectiveCalls
31 | |import scala.util.Try
32 | |
33 | |${ ctx.include().asScala.map(visitInclude) mkString "\n" }
34 | |
35 | |${ ctx.declaration().asScala.map(visitDeclaration) mkString "\n" }
36 | """.stripMargin
37 | }
38 |
39 | override def visitInclude(ctx: CausticParser.IncludeContext): String =
40 | s"${ ctx.getText }._"
41 |
42 | override def visitStruct(ctx: CausticParser.StructContext): String =
43 | s"""${ GenInternal(this.universe).visitStruct(ctx) }
44 | |${ GenExternal(this.universe).visitStruct(ctx) }
45 | """.stripMargin
46 |
47 | override def visitService(ctx: CausticParser.ServiceContext): String =
48 | s"""${ GenInternal(this.universe).visitService(ctx) }
49 | |${ GenExternal(this.universe).visitService(ctx) }
50 | """.stripMargin
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/gen/GenComment.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.gen
2 |
3 | import caustic.grammar._
4 |
5 | /**
6 | * Generates a formatted Scala comment from a comment. Caustic supports both C-style comments (/**/)
7 | * and C++-style comments (//). Left aligns comment strings.
8 | */
9 | object GenComment extends CausticBaseVisitor[String] {
10 |
11 | override def visitComment(ctx: CausticParser.CommentContext): String = {
12 | if (ctx.BlockComment() != null)
13 | ctx.getText.replaceAll("\n\\s*", "\n ")
14 | else if (ctx.LineComment() != null)
15 | ctx.getText.replaceAll("\n\\s*", "\n")
16 | else
17 | visitChildren(ctx)
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/gen/GenExternal.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.gen
2 |
3 | import caustic.compiler.error._
4 | import caustic.compiler.reflect._
5 | import caustic.compiler.util._
6 | import caustic.grammar._
7 |
8 | import scala.collection.JavaConverters._
9 |
10 | /**
11 | * Generates external Scala definitions from declaration. Preserves indentation and documentation.
12 | *
13 | * @param universe Type universe.
14 | */
15 | case class GenExternal(universe: Universe) extends CausticBaseVisitor[String] {
16 |
17 | override def visitService(ctx: CausticParser.ServiceContext): String = {
18 | val name = ctx.Identifier().getText
19 |
20 | i"""${ if (ctx.comment() != null) GenComment.visitComment(ctx.comment()) else "" }
21 | |case class $name(runtime: Runtime) {
22 | |
23 | | ${ ctx.function().asScala.map(GenExternal(this.universe).visitFunction).mkString("\n") }
24 | |}
25 | """
26 | }
27 |
28 | override def visitStruct(ctx: CausticParser.StructContext): String = {
29 | val name = ctx.Identifier().getText
30 | val fields = ctx.parameters().parameter().asScala.map(_.Identifier().getText)
31 | val types = ctx.parameters().parameter().asScala.map(_.`type`()).map(visitType)
32 |
33 | i"""object $name {
34 | |
35 | | // Implicit Conversions.
36 | | implicit def reference(x: $name)(implicit context: Context): Reference[$name$$Internal] = {
37 | | val ref = Reference.Local[$name$$Internal](context.label())
38 | | ${ fields.map(f => s"ref.get('$f) := x.$f") mkString "\n " }
39 | | ref
40 | | }
41 | |
42 | | implicit object $name$$Format extends RootJsonFormat[$name] {
43 | |
44 | | def write(x: $name): JsValue = JsObject(
45 | | ${ fields.map(f => s""""$f" -> x.$f.toJson""") mkString ", " }
46 | | )
47 | |
48 | | def read(x: JsValue): $name = {
49 | | x.asJsObject.getFields(${ fields.map(f => s""""$f"""") mkString ", " }) match {
50 | | case Seq(${ fields mkString "," }) =>
51 | | $name(${ fields.zip(types) map { case (n, t) => s"$n.convertTo[$t]" } mkString ", " })
52 | | case _ => throw DeserializationException("$name expected, but not found.")
53 | | }
54 | | }
55 | |
56 | | }
57 | |
58 | |}
59 | |
60 | |import $name._
61 | |
62 | |${ if (ctx.comment() != null) GenComment.visitComment(ctx.comment()) else "" }
63 | |case class $name(
64 | | ${ visitParameters(ctx.parameters()) }
65 | |)
66 | """
67 | }
68 |
69 | override def visitFunction(ctx: CausticParser.FunctionContext): String = {
70 | val name = ctx.Identifier().getText
71 | val args = ctx.parameters().parameter().asScala.map(_.Identifier().getText)
72 | val call = s"$name$$Internal(${ args.mkString(", ") })"
73 | val body = GenType(this.universe).visitType(ctx.`type`()) match {
74 | case CPointer(_) => s"Return($call.key.asJson)"
75 | case CUnit => call
76 | case _ => s"Return($call.asJson)"
77 | }
78 |
79 | i"""${ if (ctx.comment() != null) GenComment.visitComment(ctx.comment()) else "" }
80 | |def $name(
81 | | ${ visitParameters(ctx.parameters()) }
82 | |): Try[${ visitType(ctx.`type`()) }] = {
83 | | this.runtime evaluate { implicit context =>
84 | | $body
85 | | } map {
86 | | case Text(x) => x
87 | | case Real(x) => x.toString
88 | | case Flag(x) => x.toString
89 | | case Null => "null"
90 | | } map {
91 | | _.parseJson.convertTo[${ visitType(ctx.`type`()) }]
92 | | }
93 | |}
94 | """
95 | }
96 |
97 | override def visitParameters(ctx: CausticParser.ParametersContext): String =
98 | ctx.parameter().asScala.map(visitParameter).mkString(",\n")
99 |
100 | override def visitParameter(ctx: CausticParser.ParameterContext): String =
101 | s"${ ctx.Identifier().getText }: ${ visitType(ctx.`type`()) }"
102 |
103 | override def visitType(ctx: CausticParser.TypeContext): String =
104 | this.universe.find(ctx.getText.replaceAll("\\s", "")) match {
105 | case Some(_: CPointer) => s"Pointer[${ ctx.getText.dropRight(1)}]"
106 | case Some(_: CStruct) => s"${ ctx.getText }"
107 | case Some(CList(x)) => s"scala.List[$x]"
108 | case Some(CSet(x)) => s"scala.collection.Set[$x]"
109 | case Some(CMap(k, v)) => s"scala.collection.Map[$k, $v]"
110 | case Some(CString) => "java.lang.String"
111 | case Some(CDouble) => "scala.Double"
112 | case Some(CInt) => "scala.Int"
113 | case Some(CBoolean) => "scala.Boolean"
114 | case Some(CUnit) => "scala.Unit"
115 | case Some(any) => throw Error.Type(s"Field cannot be of type $any.", Trace(ctx))
116 | case None => throw Error.Type(s"Unknown type ${ ctx.getText }.", Trace(ctx))
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/gen/GenInternal.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.gen
2 |
3 | import caustic.compiler.error._
4 | import caustic.compiler.reflect._
5 | import caustic.compiler.util._
6 | import caustic.grammar._
7 |
8 | import scala.collection.JavaConverters._
9 |
10 | /**
11 | * Generates internal Scala definitions from declarations. Preserves indentation.
12 | *
13 | * @param universe Type universe.
14 | */
15 | case class GenInternal(universe: Universe) extends CausticBaseVisitor[String] {
16 |
17 | override def visitService(ctx: CausticParser.ServiceContext): String = {
18 | val name = ctx.Identifier().getText
19 |
20 | i"""object $name {
21 | |
22 | | ${ ctx.function().asScala.map(GenInternal(this.universe.child).visitFunction).mkString("\n") }
23 | |}
24 | |
25 | |import $name._
26 | """
27 | }
28 |
29 | override def visitStruct(ctx: CausticParser.StructContext): String = {
30 | val name = ctx.Identifier().getText
31 | val params = ctx.parameters().parameter().asScala.map(_.Identifier().getText)
32 | val fields = GenParameters(this.universe).visitParameters(ctx.parameters())
33 | val struct = CStruct(fields)
34 |
35 | this.universe.bind(name, struct)
36 | this.universe.bind(s"$name$$Constructor", CFunction(s"$name$$Internal", fields.values.toList, struct))
37 | this.universe.bind(s"$name&", CPointer(struct))
38 | this.universe.bind(s"$name&$$Constructor", CFunction(s"Reference.Remote[$name$$Internal]", List(CString), CPointer(struct)))
39 |
40 | i"""object $name$$Internal {
41 | |
42 | | implicit def reference(x: $name$$Internal)(
43 | | implicit context: Context
44 | | ): Reference[$name$$Internal] = {
45 | | val ref = Reference.Local[$name$$Internal](context.label())
46 | | ${ params.map(f => s"ref.get('$f) := x.$f") mkString "\n " }
47 | | ref
48 | | }
49 | |
50 | |}
51 | |
52 | |import $name$$Internal._
53 | |
54 | |case class $name$$Internal(
55 | | ${ visitParameters(ctx.parameters()) }
56 | |)
57 | """
58 | }
59 |
60 | override def visitFunction(ctx: CausticParser.FunctionContext): String = {
61 | val name = ctx.Identifier().getText
62 | val args = GenParameters(this.universe).visitParameters(ctx.parameters())
63 | val body = this.universe.child
64 | args foreach { case (n, t) => body.bind(n, CVariable(t)) }
65 | val returns = GenType(this.universe).visitType(ctx.`type`())
66 | this.universe.bind(name, CFunction(s"$name$$Internal", args.values.toList, returns))
67 |
68 | i"""def $name$$Internal(
69 | | ${ visitParameters(ctx.parameters()) }
70 | |)(
71 | | implicit context: Context
72 | |): ${ visitType(ctx.`type`()) } = {
73 | | ${ GenBlock(body).visitBlock(ctx.block()).value }
74 | |}
75 | """
76 | }
77 |
78 | override def visitParameters(ctx: CausticParser.ParametersContext): String =
79 | ctx.parameter().asScala.map(visitParameter).mkString(",\n")
80 |
81 | override def visitParameter(ctx: CausticParser.ParameterContext): String =
82 | s"${ ctx.Identifier().getText }: ${ visitType(ctx.`type`()) }"
83 |
84 | override def visitType(ctx: CausticParser.TypeContext): String =
85 | this.universe.find(ctx.getText.replaceAll("\\s", "")) match {
86 | case Some(CPointer(_: Primitive)) => s"Variable[${ ctx.getText.dropRight(1) }]"
87 | case Some(CPointer(_: Collection)) => s"${ ctx.getText.dropRight(1) }"
88 | case Some(_: CPointer) => s"Reference[${ ctx.getText.dropRight(1) }$$Internal]"
89 | case Some(_: CStruct) => s"Reference[${ ctx.getText }$$Internal]"
90 | case Some(CList(x)) => s"List[$x]"
91 | case Some(CSet(x)) => s"Set[$x]"
92 | case Some(CMap(k, v)) => s"Map[$k, $v]"
93 | case Some(CString) => "Value[String]"
94 | case Some(CDouble) => "Value[Double]"
95 | case Some(CInt) => "Value[Int]"
96 | case Some(CBoolean) => "Value[Boolean]"
97 | case Some(CUnit) => "Unit"
98 | case Some(any) => throw Error.Type(s"Field cannot be of type $any.", Trace(ctx))
99 | case None => throw Error.Type(s"Unknown type ${ ctx.getText }.", Trace(ctx))
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/gen/GenParameters.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.gen
2 |
3 | import caustic.compiler.reflect._
4 | import caustic.grammar._
5 |
6 | import scala.collection.JavaConverters._
7 |
8 | /**
9 | * Generates statically-typed symbol tables from parameters.
10 | *
11 | * @param universe Type universe.
12 | */
13 | case class GenParameters(universe: Universe) extends CausticBaseVisitor[Map[String, Simple]] {
14 |
15 | override def visitParameters(ctx: CausticParser.ParametersContext): Map[String, Simple] = {
16 | ctx.parameter().asScala
17 | .map(p => p.Identifier().getText -> p.`type`())
18 | .toMap
19 | .mapValues(GenType(this.universe).visitType)
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/gen/GenType.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.gen
2 |
3 | import caustic.compiler.error._
4 | import caustic.compiler.reflect._
5 | import caustic.grammar._
6 |
7 | /**
8 | * Generates a static type from a type.
9 | *
10 | * @param universe Type universe.
11 | */
12 | case class GenType(universe: Universe) extends CausticBaseVisitor[Type] {
13 |
14 | override def visitBlock(ctx: CausticParser.BlockContext): Type =
15 | GenBlock(this.universe).visitBlock(ctx).of
16 |
17 | override def visitType(ctx: CausticParser.TypeContext): Simple =
18 | this.universe.find(ctx.getText.replaceAll("\\s", "")) match {
19 | case Some(value: Simple) =>
20 | value
21 | case Some(any) =>
22 | throw Error.Type(s"Field cannot be of type $any.", Trace(ctx))
23 | case None =>
24 | throw Error.Type(s"Unknown type ${ ctx.getText }.", Trace(ctx))
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/reflect/Result.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.reflect
2 |
3 | /**
4 | * A type-tagged value.
5 | *
6 | * @param of Type.
7 | * @param value Code.
8 | */
9 | case class Result(of: Type, value: String)
10 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/reflect/binding.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.reflect
2 |
3 | /**
4 | * A compiler binding. Bindings store static type information that the compiler uses to perform type
5 | * inference. Caustic has a relatively simple static type system. A type is a binding that may
6 | * be the result of an expression. A simple type may be passed to or returned by a function or be
7 | * used as a field of a struct. A record type may have fields. A built-in type is a compiler
8 | * provided record and may represent either a primitive value or a collection of values. Programs
9 | * may define four kinds of bindings. A variable binds a name to a type. A struct binds a name to a
10 | * collection of fields. A function binds a name to a list of arguments and a return type all of
11 | * simple type. A service binds a name to a collection of functions.
12 | */
13 | sealed trait Binding
14 | sealed trait Type extends Binding
15 | sealed trait Simple extends Type
16 | sealed trait Record extends Simple { def fields: Map[String, Type] }
17 | sealed trait BuiltIn extends Record
18 | sealed trait Primitive extends BuiltIn
19 | sealed trait Collection extends BuiltIn
20 |
21 | // null
22 | case object CNull extends Primitive {
23 | override def toString: String = "null"
24 | override val fields: Map[String, Type] = Map.empty
25 | }
26 |
27 | // Unit
28 | case object CUnit extends Primitive {
29 | override def toString: String = "Unit"
30 | override val fields: Map[String, Type] = Map.empty
31 | }
32 |
33 | // Boolean
34 | case object CBoolean extends Primitive {
35 | override def toString: String = "Boolean"
36 | override val fields: Map[String, Type] = Map.empty
37 | }
38 |
39 | // Int
40 | case object CInt extends Primitive {
41 | override def toString: String = "Int"
42 | override val fields: Map[String, Type] = Map(
43 | "max" -> CFunction("max", List(CInt), CInt),
44 | "min" -> CFunction("min", List(CInt), CInt)
45 | )
46 | }
47 |
48 | // Double
49 | case object CDouble extends Primitive {
50 | override def toString: String = "Double"
51 | override val fields: Map[String, Type] = Map(
52 | "max" -> CFunction("max", List(CDouble), CDouble),
53 | "min" -> CFunction("min", List(CDouble), CDouble)
54 | )
55 | }
56 |
57 | // String
58 | case object CString extends Primitive {
59 | override def toString: String = "String"
60 | override val fields: Map[String, Type] = Map(
61 | "charAt" -> CFunction("charAt", List(CInt), CString),
62 | "contains" -> CFunction("contains", List(CString), CBoolean),
63 | "indexOf" -> CFunction("indexOf", List(CString), CInt),
64 | "length" -> CInt,
65 | "matches" -> CFunction("matches", List(CString), CBoolean),
66 | "quoted" -> CString,
67 | "substring" -> CFunction("substring", List(CInt, CInt), CString)
68 | )
69 | }
70 |
71 | // List
72 | case class CList(value: Primitive) extends Collection {
73 | override def toString: String = s"List[$value]"
74 | override def fields: Map[String, Type] = Map(
75 | "contains" -> CFunction("contains", List(value), CBoolean),
76 | "get" -> CFunction("apply", List(CInt), value),
77 | "find" -> CFunction("find", List(value), CInt),
78 | "indexOf" -> CFunction("indexOf", List(value), CInt),
79 | "remove" -> CFunction("remove", List(value), CUnit),
80 | "set" -> CFunction("set", List(CInt, value), CUnit),
81 | "size" -> CInt
82 | )
83 | }
84 |
85 | // Set
86 | case class CSet(value: Primitive) extends Collection {
87 | override def toString: String = s"Set[$value]"
88 | override def fields: Map[String, Type] = Map(
89 | "add" -> CFunction("add", List(value), CUnit),
90 | "contains" -> CFunction("contains", List(value), CBoolean),
91 | "diff" -> CFunction("diff", List(CSet(value)), CSet(value)),
92 | "intersect" -> CFunction("intersect", List(CSet(value)), CSet(value)),
93 | "remove" -> CFunction("remove", List(value), CUnit),
94 | "size" -> CInt,
95 | "union" -> CFunction("union", List(CSet(value)), CSet(value))
96 | )
97 | }
98 |
99 | // Map
100 | case class CMap(key: Primitive, value: Primitive) extends Collection {
101 | override def toString: String = s"Map[$key, $value]"
102 | override def fields: Map[String, Type] = Map(
103 | "get" -> CFunction("apply", List(CInt), value),
104 | "exists" -> CFunction("exists", List(key), CBoolean),
105 | "find" -> CFunction("find", List(value), CInt),
106 | "keys" -> CSet(key),
107 | "remove" -> CFunction("remove", List(value), CUnit),
108 | "set" -> CFunction("set", List(key, value), CUnit),
109 | "size" -> CInt
110 | )
111 | }
112 |
113 | // struct
114 | case class CStruct(fields: Map[String, Simple]) extends Record
115 |
116 | // &
117 | case class CPointer(to: Record) extends Simple
118 |
119 | // var
120 | case class CVariable(of: Simple) extends Binding
121 |
122 | // def
123 | case class CFunction(name: String, args: List[Simple], returns: Simple) extends Type
124 |
125 | // service
126 | case class CService(functions: Map[String, CFunction]) extends Binding
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/reflect/package.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler
2 |
3 | package object reflect {
4 |
5 | /**
6 | * Returns the least upper bound of the specified primitive types.
7 | *
8 | * @param x A primitive.
9 | * @param y Another primitive.
10 | * @return Least upper bound.
11 | */
12 | def lub(x: Primitive, y: Primitive): Primitive = (x, y) match {
13 | case (CNull, _) => y
14 | case (_, CNull) => x
15 | case (CUnit , _) | (_, CUnit ) => CUnit
16 | case (CString , _) | (_, CString ) => CString
17 | case (CDouble , _) | (_, CDouble ) => CDouble
18 | case (CInt , _) | (_, CInt ) => CInt
19 | case (CBoolean, _) | (_, CBoolean) => CBoolean
20 | }
21 |
22 | /**
23 | * Returns the least upper bound of the specified types.
24 | *
25 | * @param x A type.
26 | * @param y Another type.
27 | * @return Least upper bound.
28 | */
29 | def lub(x: Type, y: Type): Type = (x, y) match {
30 | case (a, b) if a == b => a
31 | case (a: Primitive, b: Primitive) => lub(a, b)
32 | case (CPointer(a: Primitive), b: Primitive) => lub(a, b)
33 | case (a: Primitive, CPointer(b: Primitive)) => lub(a, b)
34 | case (_: CFunction, _) | (_, _: CFunction ) => CUnit
35 | case (_: CStruct , _) | (_, _: CStruct ) => CUnit
36 | }
37 |
38 | /**
39 | *
40 | * @param x
41 | * @param y
42 | * @return
43 | */
44 | def isAssignable(x: Type, y: Type): Boolean = (x, y) match {
45 | case _ if lub(x, y) == x => true
46 | case (a: Primitive, CPointer(b: Primitive)) if lub(a, b) == a => true
47 | case (CPointer(a: Primitive), b: Primitive) if lub(a, b) == a => true
48 | case (CPointer(a: Primitive), CPointer(b: Primitive)) if lub(a, b) == a => true
49 | case _ => false
50 | }
51 |
52 | /**
53 | *
54 | * @param x
55 | * @param y
56 | * @return
57 | */
58 | def isPassable(x: Seq[Type], y: Seq[Type]): Boolean = x.zip(y) forall {
59 | case (a, b) if lub(a, b) == a => true
60 | case (a: Primitive, CPointer(b: Primitive)) if lub(a, b) == a => true
61 | case (CPointer(a: Primitive), CPointer(b: Primitive)) if lub(a, b) == a => true
62 | case _ => false
63 | }
64 |
65 | /**
66 | *
67 | * @param x
68 | * @return
69 | */
70 | def isNumeric(x: Type): Boolean = x match {
71 | case CInt | CDouble | CPointer(CInt | CDouble) => true
72 | case _ => false
73 | }
74 |
75 | /**
76 | *
77 | * @param x
78 | * @return
79 | */
80 | def isPrimitive(x: Type): Boolean = x match {
81 | case _: Primitive | CPointer(_: Primitive) => true
82 | case _ => false
83 | }
84 |
85 | /**
86 | *
87 | * @param x
88 | * @return
89 | */
90 | def isBoolean(x: Type): Boolean = x match {
91 | case CBoolean | CPointer(CBoolean) => true
92 | case _ => false
93 | }
94 |
95 |
96 | // /**
97 | // * Returns the greatest lower bound of the specified simple types.
98 | // *
99 | // * @param x A type.
100 | // * @param y Another type.
101 | // * @return Greatest lower bound.
102 | // */
103 | // def glb(x: Type, y: Type): Type = (x, y) match {
104 | // case (a, b) if a == b => x
105 | // case (_: CSet , _) | (_, _: CSet ) => CUnit
106 | // case (_: CMap , _) | (_, _: CMap ) => CUnit
107 | // case (_: CFunction, _) | (_, _: CFunction) => CUnit
108 | // case (_: CPointer , _) | (_, _: CPointer ) => CUnit
109 | // case (_: CStruct , _) | (_, _: CStruct ) => CUnit
110 | // case (CBoolean , _) | (_, CBoolean ) => CBoolean
111 | // case (CInt , _) | (_, CInt ) => CInt
112 | // case (CDouble , _) | (_, CDouble ) => CDouble
113 | // case (CString , _) | (_, CString ) => CString
114 | // case (CUnit , _) | (_, CUnit ) => CUnit
115 | // }
116 | //
117 | // /**
118 | // * Returns whether or not x is a subtype of the specified type.
119 | // *
120 | // * @param x A type.
121 | // * @param of Another type.
122 | // * @return Whether or not x is a subtype.
123 | // */
124 | // def isSubtype(x: Type, of: Type): Boolean =
125 | // lub(x, of) == of
126 | //
127 | // /**
128 | // * Returns whether or not x is a supertype of the specified type.
129 | // *
130 | // * @param x A type.
131 | // * @param of Another type.
132 | // * @return Whether or not x is a supertype.
133 | // */
134 | // def isSupertype(x: Type, of: Type): Boolean =
135 | // glb(x, of) == of
136 | //
137 | // /**
138 | // * Returns whether or not x is within the specified type bounds.
139 | // *
140 | // * @param x A type.
141 | // * @param lower Lower bound.
142 | // * @param upper Upper bound.
143 | // * @return Whether or not x is in the bounds.
144 | // */
145 | // def isBetween(x: Type, lower: Type, upper: Type): Boolean =
146 | // isSupertype(x, lower) && isSubtype(x, upper)
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/util/Indenter.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler.util
2 |
3 | /**
4 | * An indentation preserving string context. Standard string interpolation does not preserve
5 | * indentation; s" $x" will indent only the first line of x and not subsequent lines. Indenters
6 | * apply the same indentation to all lines of x; i" $x" will indent all lines of x by 2.
7 | *
8 | * @see https://stackoverflow.com/a/11426477/1447029
9 | * @param context String context.
10 | */
11 | case class Indenter(context: StringContext) {
12 |
13 | def i(args: Any*): String = {
14 | val builder = new StringBuilder()
15 | val parts = context.parts.map(_.stripMargin)
16 |
17 | (parts zip args) foreach { case (part, arg) =>
18 | builder.append(part)
19 | val whitespace = builder.substring(builder.lastIndexOf("\n") + 1)
20 | val indent = if (whitespace.trim.isEmpty) whitespace.length else 0
21 | builder.append(arg.toString.replaceAll("\n(?!$)", "\n" + " " * indent))
22 | }
23 |
24 | if (parts.size > args.size) builder.append(parts.last).toString else builder.toString
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/caustic-compiler/src/main/scala/caustic/compiler/util/package.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler
2 |
3 | import scala.language.implicitConversions
4 |
5 | package object util {
6 |
7 | // Implicit Conversions.
8 | implicit def indenter(context: StringContext): Indenter = Indenter(context)
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/tmbundle/Preferences/balance.tmPreferences:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | balance
7 | scope
8 | source.caustic
9 | settings
10 |
11 | highlightPairs
12 |
13 |
14 | (
15 | )
16 |
17 |
18 | {
19 | }
20 |
21 |
22 | "
23 | "
24 |
25 |
26 | `
27 | `
28 |
29 |
30 |
31 | uuid
32 | 8AFA98A5-44FB-4C83-A5A3-7D7E920F7A39
33 |
34 |
35 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/tmbundle/Snippets/if.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | if $1 {
7 | $2
8 | }
9 | name
10 | if
11 | scope
12 | source.caustic
13 | tabTrigger
14 | if
15 | uuid
16 | B1C7E50F-CE6E-44A3-A88F-CBE8A610F7FA
17 |
18 |
19 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/tmbundle/Snippets/while.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | while $1 {
7 | $2
8 | }
9 | name
10 | while
11 | scope
12 | source.caustic
13 | tabTrigger
14 | while
15 | uuid
16 | AA3141C5-2F29-4671-B82D-D68546AFB79D
17 |
18 |
19 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/tmbundle/Syntaxes/Caustic.tmLanguage:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | fileTypes
6 |
7 | acid
8 |
9 | keyEquivalent
10 | ^@C
11 | name
12 | Caustic
13 | patterns
14 |
15 |
16 | begin
17 | /\*
18 | end
19 | \*/
20 | name
21 | comment.block.caustic
22 |
23 |
24 | match
25 | \s*((//).*$\n?)
26 | name
27 | comment.line.caustic
28 |
29 |
30 | match
31 | \b(def|del|elif|else|if|import|module|rollback|service|struct|while|var)\b
32 | name
33 | keyword.control.caustic
34 |
35 |
36 | match
37 | ==?|!=|<=|>=|<>|<|>
38 | name
39 | keyword.operator.comparison.caustic
40 |
41 |
42 | match
43 | \-|\+|\*|/(?![/*])|%
44 | name
45 | keyword.operator.arithmetic.caustic
46 |
47 |
48 | match
49 | !|&&|\|\|
50 | name
51 | keyword.operator.logical.scala
52 |
53 |
54 | match
55 | \b(false|null|true)\b
56 | name
57 | constant.language.caustic
58 |
59 |
60 | match
61 | \b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)([LlFfUuDd]|UL|ul)?\b
62 | name
63 | constant.numeric.scala
64 |
65 |
66 | begin
67 | "
68 | end
69 | "
70 | name
71 | string.quoted.double.caustic
72 | patterns
73 |
74 |
75 | match
76 | \\(?:[btnfr\\"']|[0-7]{1,3}|u[0-9A-Fa-f]{4})
77 | name
78 | constant.character.escape.caustic
79 |
80 |
81 |
82 |
83 | match
84 | \b(Unit|Boolean|Int|Double|String)\b
85 | name
86 | storage.type.primitive.caustic
87 |
88 |
89 | scopeName
90 | source.caustic
91 | uuid
92 | 6C83F0BC-0075-4AE2-97C1-D74E389ECF3A
93 |
94 |
95 |
--------------------------------------------------------------------------------
/caustic-compiler/src/main/tmbundle/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | contactEmailRot13
6 | ashwin.madavan@gmail.com
7 | contactName
8 | Ashwin Madavan
9 | description
10 | New shiny bundle for Caustic.
11 | name
12 | Caustic
13 | uuid
14 | 497530BD-F700-48A8-BECF-935C0DA33954
15 |
16 |
17 |
--------------------------------------------------------------------------------
/caustic-compiler/src/test/scala/BUILD:
--------------------------------------------------------------------------------
1 | junit_tests(
2 | name='scala',
3 | sources=rglobs('*.scala'),
4 | dependencies=[
5 | 'caustic-compiler/src/main/scala',
6 | 'caustic-library/src/main/scala',
7 | '3rdparty/jvm:junit',
8 | '3rdparty/jvm:mockito',
9 | '3rdparty/jvm:scala-compiler',
10 | '3rdparty/jvm:scalatest',
11 | '3rdparty/jvm:spray-json',
12 | ],
13 | )
--------------------------------------------------------------------------------
/caustic-compiler/src/test/scala/caustic/compiler/CausticcTest.scala:
--------------------------------------------------------------------------------
1 | package caustic.compiler
2 |
3 | import org.junit.runner.RunWith
4 | import org.scalatest.Matchers
5 | import org.scalatest.FunSuite
6 | import org.scalatest.junit.JUnitRunner
7 |
8 | import scala.reflect.runtime._
9 | import scala.tools.reflect.ToolBox
10 |
11 | @RunWith(classOf[JUnitRunner])
12 | class CausticcTest extends FunSuite with Matchers {
13 |
14 | /**
15 | * A compiler for the Scala programming language.
16 | */
17 | def Scalac(source: String): Any = {
18 | val toolbox = currentMirror.mkToolBox()
19 | toolbox.eval(toolbox.parse(source))
20 | }
21 |
22 | test("Record declarations") {
23 | Causticc {
24 | s"""struct Bar {
25 | | a: String
26 | |}
27 | |
28 | |struct Foo {
29 | | b: Int,
30 | | c: Bar&,
31 | | d: Bar
32 | |}
33 | """.stripMargin
34 | } map Scalac should be a 'success
35 | }
36 |
37 | test("Service declarations") {
38 | Causticc {
39 | s"""service Decrement {
40 | | def apply(x: Int): Int = x - 1
41 | |}
42 | """.stripMargin
43 | } map Scalac should be a 'success
44 | }
45 |
46 | test("Variable definitions") {
47 | Causticc {
48 | s"""service Increment {
49 | | def apply(x: Int): Int = {
50 | | var y = x + 1
51 | | y
52 | | }
53 | |}
54 | """.stripMargin
55 | } map Scalac should be a 'success
56 | }
57 |
58 | test("Conditional branching") {
59 | Causticc {
60 | s"""service AbsoluteValue {
61 | | def apply(x: Int): Int = if (x < 0) -x else x
62 | |}
63 | """.stripMargin
64 | } map Scalac should be a 'success
65 | }
66 |
67 | test("Loops") {
68 | Causticc {
69 | s"""service Factorial {
70 | | def apply(x: Int): Int = {
71 | | var factorial = 1
72 | | var y = x
73 | |
74 | | while (y >= 2) {
75 | | factorial *= y
76 | | y -= 1
77 | | }
78 | |
79 | | factorial
80 | | }
81 | |}
82 | """.stripMargin
83 | } map Scalac should be a 'success
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/caustic-example/src/main/caustic/BUILD:
--------------------------------------------------------------------------------
1 | caustic_library(
2 | name='caustic',
3 | sources=rglobs('*.acid'),
4 | )
5 |
--------------------------------------------------------------------------------
/caustic-example/src/main/caustic/caustic/example/counter.acid:
--------------------------------------------------------------------------------
1 | module caustic.example
2 |
3 | /**
4 | * A distributed counter.
5 | */
6 | service Counter {
7 |
8 | /**
9 | * Increments the total and returns its current value.
10 | *
11 | * @param x Counter.
12 | * @return Current value.
13 | */
14 | def increment(x: Int&): Int = {
15 | if (x != null) x += 1 else x = 1
16 | x
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/caustic-example/src/main/caustic/caustic/example/fs.acid:
--------------------------------------------------------------------------------
1 | module caustic.example
2 |
3 | /**
4 | * A file.
5 | *
6 | * @param contents File contents.
7 | */
8 | struct File {
9 | contents: String
10 | }
11 |
12 | /**
13 | * A distributed file system.
14 | */
15 | service FileSystem {
16 |
17 | /**
18 | * Returns the contents of the specified path.
19 | *
20 | * @param path File path.
21 | * @return Current contents.
22 | */
23 | def read(path: File&): String = path.contents
24 |
25 | /**
26 | * Returns whether or not the file exists.
27 | *
28 | * @param path File path.
29 | * @return Whether or not the file exists.
30 | */
31 | def exists(path: File&): Boolean = path.contents != null
32 |
33 | /**
34 | * Updates the contents of the specified path.
35 | *
36 | * @param path File path.
37 | * @param contents File contents.
38 | */
39 | def write(path: File&, contents: String): Unit = path.contents = contents
40 |
41 | /**
42 | * Deletes the specified path.
43 | *
44 | * @param path File path.
45 | */
46 | def delete(path: File&): Unit = del path
47 |
48 | }
--------------------------------------------------------------------------------
/caustic-example/src/main/caustic/caustic/example/lock.acid:
--------------------------------------------------------------------------------
1 | module caustic.example
2 |
3 | /**
4 | * A read-write lock.
5 | *
6 | * @param readers Number of readers.
7 | * @param writers Number of writers.
8 | */
9 | struct Lock {
10 | readers: Int,
11 | writers: Int
12 | }
13 |
14 | /**
15 | * An access permit.
16 | *
17 | * @param lock Underlying lock.
18 | * @param forRead Read access.
19 | * @param forWrite Write access.
20 | */
21 | struct Permit {
22 | lock: Lock&,
23 | forRead: Boolean,
24 | forWrite: Boolean
25 | }
26 |
27 | /**
28 | * A distributed lock service.
29 | */
30 | service LockService {
31 |
32 | /**
33 | * Attempts to acquire exclusive access to the lock.
34 | *
35 | * @param lock Lock.
36 | * @return Read-write permit.
37 | */
38 | def exclusive(lock: Lock&): Permit = {
39 | if (lock.writers > 0 || lock.readers > 0) {
40 | Permit(lock, false, false)
41 | } else {
42 | lock.writers += 1
43 | Permit(lock, true, true)
44 | }
45 | }
46 |
47 | /**
48 | * Attempts to acquire shared access to the lock.
49 | *
50 | * @param lock Lock.
51 | * @return Read-only permit.
52 | */
53 | def shared(lock: Lock&): Permit = {
54 | if (lock.writers > 0) {
55 | Permit(lock, false, false)
56 | } else {
57 | lock.readers += 1
58 | Permit(lock, true, false)
59 | }
60 | }
61 |
62 | /**
63 | * Revoke the permit's access.
64 | *
65 | * @param permit Permit.
66 | */
67 | def release(permit: Permit): Unit = {
68 | if (permit.forWrite) {
69 | permit.lock.writers -= 1
70 | } elif (permit.forRead) {
71 | permit.lock.readers -= 1
72 | }
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/caustic-example/src/main/caustic/caustic/example/queue.acid:
--------------------------------------------------------------------------------
1 | module caustic.example
2 |
3 | /**
4 | * A distributed message queue.
5 | */
6 | service Queue {
7 |
8 | /**
9 | * Adds the message to the end of the queue.
10 | *
11 | * @param queue Queue.
12 | * @param message Message.
13 | */
14 | def push(queue: List[String]&, message: String): Unit =
15 | queue.set(queue.size, message)
16 |
17 | /**
18 | * Returns the message at the front of the queue.
19 | *
20 | * @param queue Queue.
21 | * @return Head.
22 | */
23 | def peek(queue: List[String]&): String =
24 | queue.get(0)
25 |
26 | /**
27 | * Removes and returns the message at the front of the queue.
28 | *
29 | * @param queue Queue.
30 | * @return Head.
31 | */
32 | def pop(queue: List[String]&): String = {
33 | var head = peek(queue)
34 | queue.set(0, null)
35 | head
36 | }
37 |
38 | /**
39 | * Returns the number of messages in the queue.
40 | *
41 | * @param queue Queue.
42 | * @return Length.
43 | */
44 | def size(queue: List[String]&): Int =
45 | queue.size
46 |
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/BUILD:
--------------------------------------------------------------------------------
1 | scala_library(
2 | name='scala',
3 | sources=rglobs('*.scala'),
4 | dependencies=[
5 | 'caustic-runtime/src/main/scala',
6 | '3rdparty/jvm:shapeless',
7 | '3rdparty/jvm:spray-json',
8 | ],
9 | provides=artifact(
10 | org='com.madavan',
11 | name='caustic-library_2.12',
12 | repo=public,
13 | publication_metadata=describe,
14 | ),
15 | )
16 |
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/Context.scala:
--------------------------------------------------------------------------------
1 | package caustic.library
2 |
3 | import caustic.library.typing._
4 | import caustic.runtime._
5 |
6 | /**
7 | * A parsing context.
8 | *
9 | * @param body Current program.
10 | */
11 | case class Context(
12 | private[library] var body: Program = Null,
13 | private[library] var current: scala.Int = -1
14 | ) {
15 |
16 | /**
17 | * Appends the program to the context.
18 | *
19 | * @param that Program to append.
20 | */
21 | def +=(that: Program): Unit =
22 | this.body = cons(this.body, that)
23 |
24 | /**
25 | *
26 | * @return
27 | */
28 | def label(): Value[String] = {
29 | this.current += 1
30 | "$" + this.current
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/control/package.scala:
--------------------------------------------------------------------------------
1 | package caustic.library
2 |
3 | import caustic.library.typing._
4 | import caustic.runtime.Runtime._
5 | import caustic.runtime._
6 |
7 | import scala.util.Try
8 |
9 | package object control {
10 |
11 | // Implicit Operations.
12 | implicit class RuntimeOps(x: Runtime) {
13 |
14 | /**
15 | * Executes the parsed program on the runtime and returns the result.
16 | *
17 | * @param f Program builder.
18 | * @throws Rollbacked If the program was rolled back.
19 | * @throws Aborted If the program could not be executed.
20 | * @throws Fault If the program is illegally constructed.
21 | * @return Literal result or exception on failure.
22 | */
23 | def evaluate[U](f: Context => U): Try[Literal] = {
24 | val context = Context()
25 | f(context)
26 | x.execute(context.body)
27 | }
28 |
29 | }
30 |
31 | /**
32 | * Asserts that the specified value is true or rollsback otherwise. Useful for testing.
33 | *
34 | * @param x Value to assert.
35 | * @param context Parse context.
36 | */
37 | def Assert(x: Value[Boolean])(implicit context: Context): Unit =
38 | If (!x) { Rollback(s"Assertion Failure: ${x.get} evaluates to false") }
39 |
40 | /**
41 | * Branches on the value of the specified condition. An Else clause may be optionally specified.
42 | * Utilizes structural types, which can be enabled by importing the scala.language.reflectiveCalls
43 | * language feature.
44 | *
45 | * @param condition Condition to branch on.
46 | * @param success Execute if condition is satisfied.
47 | * @param context Implicit transaction context.
48 | * @return Optional else clause.
49 | */
50 | def If[T](condition: Value[Boolean])(success: => T)(implicit context: Context) = new {
51 | private val before = context.body
52 | context.body = Null
53 | success
54 | private val pass = context.body
55 | context.body = before
56 | context += branch(condition, pass, Null)
57 |
58 | def Else(failure: => T): T = {
59 | context.body = Null
60 | val result = failure
61 | val fail = context.body
62 | context.body = before
63 | context += branch(condition, pass, fail)
64 | result
65 | }
66 | }
67 |
68 | /**
69 | * Adds the value to the parse context. Return does not break execution.
70 | *
71 | * @param result Value to return.
72 | * @param context Parse context.
73 | */
74 | def Return[T <: Primitive](result: Value[T])(implicit context: Context): Unit =
75 | context += result
76 |
77 | /**
78 | * Rollsback the transaction and returns the specified result. All modifications made by a program
79 | * are discarded on rollback, and program execution terminates. Rollbacked programs are guaranteed
80 | * to see a consistent snapshot of the database.
81 | *
82 | * @param result Return value.
83 | * @param context Parsing context.
84 | */
85 | def Rollback[T <: Primitive](result: Value[T])(implicit context: Context): Unit =
86 | context += rollback(result)
87 |
88 | /**
89 | * Performs the block while the condition is satisfied. Because loops are evaluated one iteration
90 | * at a time by the runtime, it is important to prefetch keys before entering the body of the
91 | * loop. Otherwise, each iteration of the loop will require a separate read on the database.
92 | *
93 | * @param condition Condition to loop on.
94 | * @param block Loop body.
95 | * @param context Implicit transaction context.
96 | */
97 | def While(condition: Value[Boolean])(block: => Unit)(implicit context: Context): Unit = {
98 | val before = context.body
99 | context.body = Null
100 | block
101 | val body = context.body
102 | context.body = before
103 | context += repeat(condition, body)
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/Constant.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 |
3 | import caustic.runtime.Program
4 |
5 | /**
6 | * A constant value.
7 | *
8 | * @param get Program representation.
9 | */
10 | case class Constant[+T <: Primitive](get: Program) extends Value[T]
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/Conversion.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 |
3 | import caustic.library.Context
4 | import caustic.library.typing.collection._
5 |
6 | /**
7 | * A conversion from Scala to Caustic.
8 | */
9 | trait Conversion[-Scala, +Caustic] {
10 |
11 | /**
12 | * Applies the conversion to the specified value.
13 | *
14 | * @param x Scala value.
15 | * @return Caustic value.
16 | */
17 | def apply(x: Scala): Caustic
18 |
19 | }
20 |
21 | object Conversion {
22 |
23 | // scala.Boolean => Boolean
24 | implicit val boolean: Conversion[scala.Boolean, Value[Boolean]] =
25 | x => Constant(x)
26 |
27 | // scala.Int => Int
28 | implicit val int: Conversion[scala.Int, Value[Int]] =
29 | x => Constant(x)
30 |
31 | // scala.Long => Int
32 | implicit val long: Conversion[scala.Long, Value[Int]] =
33 | x => Constant(x)
34 |
35 | // scala.Double => Double
36 | implicit val double: Conversion[scala.Double, Value[Double]] =
37 | x => Constant(x)
38 |
39 | // java.lang.String => String
40 | implicit val string: Conversion[java.lang.String, Value[String]] =
41 | x => Constant(x)
42 |
43 | // scala.List => List
44 | implicit def list[S, C <: Primitive](
45 | implicit context: Context,
46 | conversion: Conversion[S, Value[C]]
47 | ): Conversion[scala.List[S], List[C]] = x => {
48 | val list = List.Local[C](context.label())
49 | x.foreach(i => list += conversion(i))
50 | list
51 | }
52 |
53 | // scala.collection.Set => Set
54 | implicit def set[S, C <: Primitive](
55 | implicit context: Context,
56 | conversion: Conversion[S, Value[C]]
57 | ): Conversion[scala.collection.Set[S], Set[C]] = x => {
58 | val set = Set.Local[C](context.label())
59 | x.foreach(i => set += conversion(i))
60 | set
61 | }
62 |
63 | // scala.collection.Map => Map
64 | implicit def map[K0, V0, K1 <: String, V1 <: Primitive](
65 | implicit context: Context,
66 | keyConversion: Conversion[K0, Value[K1]],
67 | valueConversion: Conversion[V0, Value[V1]]
68 | ): Conversion[scala.collection.Map[K0, V0], Map[K1, V1]] = x => {
69 | val map = Map.Local[K1, V1](context.label())
70 | x foreach { case (k, v) => map += keyConversion(k) -> valueConversion(v) }
71 | map
72 | }
73 |
74 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/Internal.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 |
3 | /**
4 | * A marker trait for library types.
5 | */
6 | trait Internal
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/Pointer.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 |
3 | import spray.json._
4 | import DefaultJsonProtocol._
5 |
6 | /**
7 | * A pointer to an object stored at the specified location.
8 | *
9 | * @param key Location.
10 | */
11 | case class Pointer[T](key: java.lang.String)
12 |
13 | object Pointer {
14 |
15 | // Implicit Conversions.
16 | implicit def format[T]: JsonFormat[Pointer[T]] = new JsonFormat[Pointer[T]] {
17 | override def write(x: Pointer[T]): JsValue = x.key.toJson
18 | override def read(x: JsValue): Pointer[T] = Pointer(x.convertTo[java.lang.String])
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/Value.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 |
3 | import caustic.runtime
4 | import caustic.runtime._
5 |
6 | /**
7 | * A scalar value.
8 | */
9 | trait Value[+T <: Primitive] extends Internal {
10 |
11 | /**
12 | * Returns the value as a program.
13 | *
14 | * @return Program representation.
15 | */
16 | def get: Program
17 |
18 | }
19 |
20 | object Value {
21 |
22 | // Implicit Operations.
23 | implicit class AdditionOps[X <: Primitive](x: Value[X]) {
24 | def +[Y <: X](y: Value[Y])(implicit evidence: Y <:< X): Value[X] = add(x, y)
25 | def +[Y >: X <: Primitive](y: Value[Y]): Value[Y] = add(x, y)
26 | }
27 |
28 | implicit class ArithmeticOps[X >: Int <: Double](x: Value[X]) {
29 | def unary_- : Value[X] = sub(0, x)
30 | def -(y: Value[Int]): Value[X] = sub(x, y)
31 | def -[Y >: X <: Double](y: Value[Y])(implicit evidence: X <:< Y): Value[Y] = sub(x, y)
32 | def *(y: Value[Int]): Value[X] = mul(x, y)
33 | def *[Y >: X <: Double](y: Value[Y])(implicit evidence: X <:< Y): Value[Y] = mul(x, y)
34 | def /(y: Value[Int]): Value[X] = div(x, y)
35 | def /[Y >: X <: Double](y: Value[Y])(implicit evidence: X <:< Y): Value[Y] = div(x, y)
36 | def %(y: Value[Int]): Value[X] = mod(x, y)
37 | def %[Y >: X <: Double](y: Value[Y])(implicit evidence: X <:< Y): Value[Y] = mod(x, y)
38 | def asJson: Value[String] = branch(x, x, "null")
39 | }
40 |
41 | implicit class TextualOps[X >: String <: Primitive](x: Value[X]) {
42 | def charAt(y: Value[Int]): Value[String] = x.substring(y, y + 1)
43 | def contains(y: Value[String]): Value[Boolean] = runtime.contains(x, y)
44 | def indexOf(y: Value[String]): Value[Int] = runtime.indexOf(x, y)
45 | def length: Value[Int] = runtime.length(x)
46 | def matches(y: Value[String]): Value[Boolean] = runtime.matches(x, y)
47 | def quoted: Value[String] = add("\"", add(x, "\""))
48 | def substring(l: Value[Int], h: Value[Int] = length): Value[String] = slice(x, l, h)
49 | def asJson: Value[String] = branch(x, x.quoted, "null")
50 | }
51 |
52 | implicit class ComparisonOps[X <: Primitive](x: Value[X]) {
53 | def <[Y <: Primitive](y: Value[Y]): Value[Boolean] = less(x, y)
54 | def >[Y <: Primitive](y: Value[Y]): Value[Boolean] = !(x <= y)
55 | def <=[Y <: Primitive](y: Value[Y]): Value[Boolean] = x < y || x === y
56 | def >=[Y <: Primitive](y: Value[Y]): Value[Boolean] = !(x < y)
57 | def <>[Y <: Primitive](y: Value[Y]): Value[Boolean] = !(x === y)
58 | def ===[Y <: Primitive](y: Value[Y]): Value[Boolean] = equal(x, y)
59 | def max(y: Value[X]): Value[X] = branch(x < y, y, x)
60 | def min(y: Value[X]): Value[X] = branch(x < y, x, y)
61 | }
62 |
63 | implicit class LogicalOps[X <: Boolean](x: Value[X]) {
64 | def unary_! : Value[X] = negate(x)
65 | def &&(y: Value[X]): Value[X] = both(x, y)
66 | def ||(y: Value[X]): Value[X] = either(x, y)
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/Variable.scala:
--------------------------------------------------------------------------------
1 | package caustic.library
2 | package typing
3 |
4 | import caustic.library.typing.Value._
5 | import caustic.runtime._
6 |
7 | /**
8 | * A scalar variable.
9 | */
10 | sealed trait Variable[T <: Primitive] extends Value[T] {
11 |
12 | /**
13 | * Returns the name of the variable.
14 | *
15 | * @return Variable name.
16 | */
17 | def key: Value[String]
18 |
19 | /**
20 | * Updates the value of the variable.
21 | *
22 | * @param that Updated value.
23 | * @param context Parsing context.
24 | */
25 | def set(that: Value[T])(implicit context: Context): Unit
26 |
27 | /**
28 | * Scopes the variable with the specified namespace.
29 | *
30 | * @param x Namespace.
31 | * @return Scoped variable.
32 | */
33 | def scope[U <: Primitive](x: Value[String]): Variable[U] = this match {
34 | case Variable.Local(k) => Variable.Local(k + "/" + x)
35 | case Variable.Remote(k) => Variable.Remote(k + "/" + x)
36 | }
37 |
38 | }
39 |
40 | object Variable {
41 |
42 | /**
43 | * A local variable.
44 | *
45 | * @param key Variable name.
46 | */
47 | case class Local[T <: Primitive](key: Value[String]) extends Variable[T] {
48 | override def get: Program = load(key)
49 | override def set(that: Value[T])(implicit context: Context): Unit = context += store(key, that)
50 | }
51 |
52 | /**
53 | * A remote variable.
54 | *
55 | * @param key Variable name.
56 | */
57 | case class Remote[T <: Primitive](key: Value[String]) extends Variable[T] {
58 | override def get: Program = read(key)
59 | override def set(that: Value[T])(implicit context: Context): Unit = context += write(key, that)
60 | }
61 |
62 | // Implicit Operations.
63 | implicit class AssignmentOps[X <: Primitive](x: Variable[X]) {
64 | def :=[Y <: X](y: Value[Y])(implicit context: Context): Unit = x.set(y)
65 | }
66 |
67 | implicit class CompoundAssignment[X >: Int <: Double](x: Variable[X]) {
68 | def +=[Y <: X](y: Value[Y])(implicit context: Context): Unit = x := (x + y)
69 | def -=[Y <: X](y: Value[Y])(implicit context: Context): Unit = x := (x - y)
70 | def *=[Y <: X](y: Value[Y])(implicit context: Context): Unit = x := (x * y)
71 | def /=[Y <: X](y: Value[Y])(implicit context: Context): Unit = x := (x / y)
72 | def %=[Y <: X](y: Value[Y])(implicit context: Context): Unit = x := (x % y)
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/collection/Collection.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 | package collection
3 |
4 | import caustic.library.Context
5 | import caustic.library.control._
6 |
7 | import caustic.runtime.Null
8 |
9 | /**
10 | * An indexed collection of values.
11 | */
12 | trait Collection[K <: Primitive, V <: Primitive] extends Internal {
13 |
14 | /**
15 | * Returns the value at the specified index.
16 | *
17 | * @param key Index.
18 | * @param context Parse context.
19 | * @return Corresponding value or [[Null]].
20 | */
21 | def apply(key: Value[K])(implicit context: Context): Value[V] = get(key)
22 |
23 | /**
24 | * Serializes the collection to a JSON string.
25 | *
26 | * @param context Parse context.
27 | * @return Serialized representation.
28 | */
29 | def asJson(implicit context: Context): Value[String]
30 |
31 | /**
32 | * Returns whether or not the collection contains the specified value.
33 | *
34 | * @param value Value.
35 | * @param context Parse context.
36 | * @return Whether or not the value is in the collection.
37 | */
38 | def contains(value: Value[V])(implicit context: Context): Value[Boolean] = find(value) <> null
39 |
40 | /**
41 | * Clears the contents of the collection.
42 | *
43 | * @param context Parse context.
44 | */
45 | def delete()(implicit context: Context): Unit = foreach { case (k, _) => set(k, Null) }
46 |
47 | /**
48 | * Returns whether or not the specified index is present in the collection.
49 | *
50 | * @param key Index.
51 | * @param context Parse context.
52 | * @return Whether or not the key is in the collection.
53 | */
54 | def exists(key: Value[K])(implicit context: Context): Value[Boolean] = {
55 | val x = Variable.Local[Boolean](context.label())
56 | x := False
57 | foreach { case (k, _) => x := x || k === key }
58 | x
59 | }
60 |
61 | /**
62 | * Returns the first key that corresponds to the specified value.
63 | *
64 | * @param value Value.
65 | * @param context Parse context.
66 | * @return Index of the value or [[Null]].
67 | */
68 | def find(value: Value[V])(implicit context: Context): Value[K] = {
69 | val index = Variable.Local[K](context.label())
70 | index := Null
71 | foreach { case (i, v) => If (index === Null && v === value)(index := i) }
72 | index
73 | }
74 |
75 | /**
76 | * Applies the specified function to each entry in the collection.
77 | *
78 | * @param f Function.
79 | * @param context Parse context.
80 | */
81 | def foreach[U](f: (Value[K], Value[V]) => U)(implicit context: Context): Unit
82 |
83 | /**
84 | * Returns the value at the specified index.
85 | *
86 | * @param key Index.
87 | * @param context Parse context.
88 | * @return Corresponding value.
89 | */
90 | def get(key: Value[K])(implicit context: Context): Value[V]
91 |
92 | /**
93 | * Updates the value at the specified index.
94 | *
95 | * @param key Index.
96 | * @param value Updated value.
97 | * @param context Parse context.
98 | */
99 | def set(key: Value[K], value: Value[V])(implicit context: Context): Unit
100 |
101 | /**
102 | * Returns the number of values in the collection.
103 | *
104 | * @return Length.
105 | */
106 | def size: Value[Int]
107 |
108 | /**
109 | * Removes the first occurrence of the specified value from the collection.
110 | *
111 | * @param value Removed value.
112 | * @param context Parse context.
113 | */
114 | def remove(value: Value[V])(implicit context: Context): Unit = {
115 | val key = find(value)
116 | If (key <> Null) { set(key, Null) }
117 | }
118 |
119 | /**
120 | * Returns whether or not this collection contains the same entries as the specified collection.
121 | *
122 | * @param that Collection.
123 | * @param context Parse context.
124 | * @return Whether or not the collections are equal.
125 | */
126 | def ===(that: Collection[K, V])(implicit context: Context): Value[Boolean] = {
127 | val x = Variable.Local[Boolean](context.label())
128 | x := this.size === that.size
129 | this foreach { case (k, _) => x := x && this(k) === that(k) }
130 | x
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/collection/List.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 | package collection
3 |
4 | import caustic.library.Context
5 | import caustic.library.control._
6 | import caustic.library.typing.Value._
7 |
8 | import caustic.runtime._
9 | import scala.language.reflectiveCalls
10 |
11 | /**
12 | * A mutable collection of values.
13 | *
14 | * @param length Current size.
15 | */
16 | class List[T <: Primitive](length: Variable[Int]) extends Collection[Int, T] {
17 |
18 | override def size: Value[Int] = this.length
19 |
20 | override def get(key: Value[Int])(implicit context: Context): Value[T] =
21 | this.length.scope[T](key)
22 |
23 | override def set(key: Value[Int], value: Value[T])(implicit context: Context): Unit = {
24 | If (exists(key) && value === Null) {
25 | // Shift over all values after the index of the removed value.
26 | foreach { case (i, _) =>
27 | If (i >= key && i < this.length - 1) {
28 | this.length.scope[T](i) := get(i + 1)
29 | }
30 | }
31 |
32 | // Decrement the size of the list.
33 | this.length.scope[T](this.length - 1) := Null
34 | this.length -= 1
35 | } Else {
36 | // Update the value of the key.
37 | this.length.scope[T](key) := value
38 | this.length := (this.length max key) + 1
39 | }
40 | }
41 |
42 | override def foreach[U](f: (Value[Int], Value[T]) => U)(implicit context: Context): Unit = {
43 | // Prefetch the list if it is remotely stored.
44 | this.length match {
45 | case _: Variable.Remote[Int] => context += prefetch(this.length.key, this.length, 1)
46 | case _ =>
47 | }
48 |
49 | // Iterate through each item in the list.
50 | val index = Variable.Local[Int](context.label())
51 | index := 0
52 |
53 | While (index < this.length) {
54 | f(index, this(index))
55 | }
56 | }
57 |
58 | override def asJson(implicit context: Context): Value[String] = {
59 | val json = Variable.Local[String](context.label())
60 | json := "["
61 |
62 | foreach { case (_, v) =>
63 | If (json === "[") {
64 | json := json + v.asJson
65 | } Else {
66 | json := json + ", " + v.asJson
67 | }
68 | }
69 |
70 | json + "]"
71 | }
72 |
73 | }
74 |
75 | object List {
76 |
77 | /**
78 | * Returns a list backed by the specified variable.
79 | *
80 | * @param key Underlying variable.
81 | * @return Initialized list.
82 | */
83 | def apply[T <: Primitive](key: Variable[Int]): List[T] = new List[T](key)
84 |
85 | /**
86 | * Returns a list backed by the specified local variable.
87 | *
88 | * @param key Local variable.
89 | * @return Local list.
90 | */
91 | def Local[T <: Primitive](key: Value[String]): List[T] = List(Variable.Local(key))
92 |
93 | /**
94 | * Returns a list backed by the specified remote variable.
95 | *
96 | * @param key Remote variable.
97 | * @return Remote list.
98 | */
99 | def Remote[T <: Primitive](key: Value[String]): List[T] = List(Variable.Remote(key))
100 |
101 | // Implicit Operations.
102 | implicit class AssignmentOps[T <: Primitive](x: List[T]) {
103 | def :=(y: List[T])(implicit context: Context): Unit = {
104 | x.delete()
105 | x ++= y
106 | }
107 | }
108 |
109 | implicit class CompoundAssignmentOps[T <: Primitive](x: List[T]) {
110 | def ++=(y: List[T])(implicit context: Context): Unit = y foreach { case (_, v) => x += _ }
111 | def --=(y: List[T])(implicit context: Context): Unit = y foreach { case (_, v) => x -= _ }
112 | def +=(y: Value[T])(implicit context: Context): Unit = x.set(x.size, y)
113 | def -=(y: Value[T])(implicit context: Context): Unit = x.remove(y)
114 | }
115 |
116 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/collection/Map.scala:
--------------------------------------------------------------------------------
1 | package caustic.library
2 | package typing
3 | package collection
4 |
5 | import caustic.library.control._
6 | import caustic.library.typing.Value._
7 | import caustic.runtime.{Null, prefetch}
8 |
9 | import scala.language.reflectiveCalls
10 |
11 | /**
12 | * A mutable collection of key-value pairs.
13 | *
14 | * @param length Current size.
15 | */
16 | class Map[K <: String, V <: Primitive](length: Variable[Int]) extends Collection[K, V] {
17 |
18 | /**
19 | * Returns the set of keys in the map.
20 | */
21 | val keys: Set[K] = new Set(length)
22 |
23 | override def size: Value[Int] = this.keys.size
24 |
25 | override def get(key: Value[K])(implicit context: Context): Value[V] =
26 | this.length.scope(this.keys.find(key)).scope(key)
27 |
28 | override def set(key: Value[K], value: Value[V])(implicit context: Context): Unit = {
29 | If (value === Null) {
30 | this.length.scope(this.keys.find(key)).scope(key) := Null
31 | this.keys.remove(key)
32 | } Else {
33 | this.keys.add(key)
34 | this.length.scope(this.keys.find(key)).scope[V](key) := value
35 | }
36 | }
37 |
38 | override def foreach[U](f: (Value[K], Value[V]) => U)(implicit context: Context): Unit = {
39 | // Prefetch all key-value pairs if the map is remotely stored.
40 | if (this.length.isInstanceOf[Variable.Remote[Int]])
41 | context += prefetch(this.length.key, this.size, 2)
42 |
43 | // Iterate over each key-value pair in the map.
44 | this.keys foreach { case (_, k) => f(k, this(k)) }
45 | }
46 |
47 | override def asJson(implicit context: Context): Value[String] = {
48 | val json = Variable.Local[String](context.label())
49 | json := "{"
50 |
51 | foreach { case (k, v) =>
52 | If (json === "{") {
53 | json := json + k.quoted + ": " + v.asJson
54 | } Else {
55 | json := json + ", " + k.quoted + ": " + v.asJson
56 | }
57 | }
58 |
59 | json + "}"
60 | }
61 |
62 | }
63 |
64 | object Map {
65 |
66 | /**
67 | * Returns a map backed by the specified variable.
68 | *
69 | * @param key Underlying variable.
70 | * @return Initialized map.
71 | */
72 | def apply[K <: String, V <: Primitive](key: Variable[Int]): Map[K, V] = new Map(key)
73 |
74 | /**
75 | * Returns a map backed by the specified local variable.
76 | *
77 | * @param key Local variable.
78 | * @return Local map.
79 | */
80 | def Local[K <: String, V <: Primitive](key: Value[String]): Map[K, V] = Map(Variable.Local(key))
81 |
82 | /**
83 | * Returns a map backed by the specified remote variable.
84 | *
85 | * @param key Remote variable.
86 | * @return Remote map.
87 | */
88 | def Remote[K <: String, V <: Primitive](key: Value[String]): Map[K, V] = Map(Variable.Remote(key))
89 |
90 | // Implicit Operations.
91 | implicit class AssignmentOps[K <: String, V <: Primitive](x: Map[K, V]) {
92 | def :=(y: Map[K, V])(implicit context: Context): Unit = { x.delete(); x ++= y }
93 | }
94 |
95 | implicit class CompoundAssignmentOps[K <: String, V <: Primitive](x: Map[K, V]) {
96 | def ++=(y: Map[K, V])(implicit context: Context): Unit = y.foreach(x.set)
97 | def --=(y: Map[K, V])(implicit context: Context): Unit = y foreach { case (k, _) => x -= k }
98 | def +=(kv: (Value[K], Value[V]))(implicit context: Context): Unit = x.set(kv._1, kv._2)
99 | def -=(k: Value[K])(implicit context: Context): Unit = x.set(k, Null)
100 | }
101 |
102 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/collection/Set.scala:
--------------------------------------------------------------------------------
1 | package caustic.library
2 | package typing
3 | package collection
4 |
5 | import caustic.library.control._
6 |
7 | import scala.language.reflectiveCalls
8 |
9 | /**
10 | * A collection of unique values.
11 | *
12 | * @param length Current size.
13 | */
14 | class Set[T <: Primitive](length: Variable[Int]) extends List[T](length) {
15 |
16 | /**
17 | * Adds the value to the set if and only if it is not already present.
18 | *
19 | * @param value Value to add.
20 | * @param context Parse context.
21 | * @return Whether or not the value was added.
22 | */
23 | def add(value: Value[T])(implicit context: Context): Value[Boolean] = {
24 | If (contains(value)) {
25 | false
26 | } Else {
27 | this += value
28 | true
29 | }
30 | }
31 |
32 | /**
33 | * Returns a set containing all elements in this set that are not present in the specified set.
34 | *
35 | * @param that Set.
36 | * @param context Parse context.
37 | * @return Set difference.
38 | */
39 | def diff(that: Set[T])(implicit context: Context): Set[T] = {
40 | val set = Set.Local[T](context.label())
41 | set ++= this
42 | set --= that
43 | set
44 | }
45 |
46 | /**
47 | * Returns a set containing all elements present in both sets.
48 | *
49 | * @param that Set.
50 | * @param context Parse context.
51 | * @return Set intersection.
52 | */
53 | def intersect(that: Set[T])(implicit context: Context): Set[T] = {
54 | val set = Set.Local[T](context.label())
55 | this foreach { case (_, v) => If (that.contains(v)) { set.add(v) } }
56 | set
57 | }
58 |
59 | /**
60 | * Returns a set containing all elements present in either set.
61 | *
62 | * @param that Set.
63 | * @param context Parse context.
64 | * @return Set union.
65 | */
66 | def union(that: Set[T])(implicit context: Context): Set[T] = {
67 | val set = Set.Local[T](context.label())
68 | set ++= this
69 | set ++= that
70 | set
71 | }
72 |
73 | }
74 |
75 | object Set {
76 |
77 | /**
78 | * Returns a set backed by the specified variable.
79 | *
80 | * @param key Underlying variable.
81 | * @return Initialized set.
82 | */
83 | def apply[T <: Primitive](key: Variable[Int]): Set[T] = new Set(key)
84 |
85 | /**
86 | * Returns a set backed by the specified local variable.
87 | *
88 | * @param key Local variable.
89 | * @return Local set.
90 | */
91 | def Local[T <: Primitive](key: Value[String]): Set[T] = Set(Variable.Local(key))
92 |
93 | /**
94 | * Returns a set backed by the specified remote variable.
95 | *
96 | * @param key Remote variable.
97 | * @return Remote set.
98 | */
99 | def Remote[T <: Primitive](key: Value[String]): Set[T] = Set(Variable.Remote(key))
100 |
101 | // Implicit Operations.
102 | implicit class AssignmentOps[T <: Primitive](x: Set[T]) {
103 | def :=(y: Set[T])(implicit context: Context): Unit = { x.delete(); x ++= y }
104 | }
105 |
106 | implicit class CompoundAssignmentOps[T <: Primitive](x: Set[T]) {
107 | def ++=(y: Set[T])(implicit context: Context): Unit = y foreach { case (_, v) => x.add(v) }
108 | def --=(y: Set[T])(implicit context: Context): Unit = y foreach { case (_, v) => x.remove(v) }
109 | def +=(y: Value[T])(implicit context: Context): Unit = x.add(y)
110 | def -=(y: Value[T])(implicit context: Context): Unit = x.remove(y)
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/package.scala:
--------------------------------------------------------------------------------
1 | package caustic.library
2 |
3 | import caustic.library.typing.Value._
4 | import caustic.library.typing.collection._
5 | import caustic.library.typing.record.Reference
6 | import caustic.runtime._
7 |
8 | import scala.language.implicitConversions
9 |
10 | package object typing {
11 |
12 | val True: Value[Boolean] = caustic.runtime.True
13 | val False: Value[Boolean] = caustic.runtime.False
14 |
15 | // Implicit Conversions.
16 | implicit def program[X <: Primitive](x: Value[X]): Program = x.get
17 | implicit def value[X <: Primitive](x: Program): Value[X] = Constant[X](x)
18 | implicit def convert[S, C](x: S)(implicit conversion: Conversion[S, C]): C = conversion(x)
19 | implicit def pointer[T](x: java.lang.String): Pointer[T] = Pointer(x)
20 | implicit def reference[T, U](x: Pointer[T]): Reference[U] = Reference.Remote(x.key)
21 | implicit def variable[T <: Primitive](x: Pointer[T]): Variable[T] = Variable.Remote(x.key)
22 | implicit def list[T <: Primitive](x: Pointer[List[T]]): List[T] = List.Remote(x.key)
23 | implicit def set[T <: Primitive](x: Pointer[Set[T]]): Set[T] = Set.Remote(x.key)
24 | implicit def map[K <: String, V <: Primitive](x: Pointer[Map[K, V]]): Map[K, V] = Map.Remote(x.key)
25 |
26 | // Implicit Operations.
27 | implicit def int2addition(x: scala.Int): AdditionOps[Int] = AdditionOps(x)
28 | implicit def long2addition(x: scala.Long): AdditionOps[Int] = AdditionOps(x)
29 | implicit def double2addition(x: scala.Double): AdditionOps[Double] = AdditionOps(x)
30 | implicit def int2arithmetic(x: scala.Int): ArithmeticOps[Int] = ArithmeticOps(x)
31 | implicit def long2arithmetic(x: scala.Long): ArithmeticOps[Int] = ArithmeticOps(x)
32 | implicit def double2arithmetic(x: scala.Double): ArithmeticOps[Double] = ArithmeticOps(x)
33 | implicit def int2comparison(x: scala.Int): ComparisonOps[Int] = ComparisonOps(x)
34 | implicit def long2comparison(x: scala.Long): ComparisonOps[Int] = ComparisonOps(x)
35 | implicit def double2comparison(x: scala.Double): ComparisonOps[Double] = ComparisonOps(x)
36 | implicit def string2comparison(x: java.lang.String): ComparisonOps[String] = ComparisonOps(x)
37 | implicit def string2textual(x: java.lang.String): TextualOps[String] = TextualOps(x)
38 | implicit def boolean2logical(x: scala.Boolean): LogicalOps[Boolean] = LogicalOps(x)
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/primitive.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 |
3 | /**
4 | * A scalar type.
5 | */
6 | sealed trait Primitive extends Internal
7 | trait String extends Primitive
8 | trait Double extends String
9 | trait Int extends Double
10 | trait Boolean extends Int
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/record/Field.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 | package record
3 |
4 | import caustic.library.typing.collection._
5 |
6 | import shapeless._
7 |
8 | /**
9 | * An attribute extractor.
10 | */
11 | trait Field[Type] {
12 | type Container
13 | def apply(key: Variable[String], field: Value[String]): Container
14 | def apply[T](ref: Reference[T], field: Value[String]): Container = apply(ref.pointer, field)
15 | }
16 |
17 | object Field {
18 |
19 | type Aux[Type, Container0] = Field[Type] { type Container = Container0 }
20 |
21 | // Implicit Conversions.
22 | implicit def value[T <: Primitive]: Aux[Value[T], Variable[T]] = new Field[Value[T]] {
23 | type Container = Variable[T]
24 | override def apply(k: Variable[String], f: Value[String]): Variable[T] = k.scope(f)
25 | }
26 |
27 | implicit def reference[T]: Aux[Reference[T], Reference[T]] = new Field[Reference[T]] {
28 | override type Container = Reference[T]
29 | override def apply(k: Variable[String], f: Value[String]): Reference[T] = Reference(k.scope(f))
30 | }
31 |
32 | implicit def record[T](implicit evidence: T <:!< Internal): Aux[T, Reference[T]] = new Field[T] {
33 | override type Container = Reference[T]
34 | override def apply(k: Variable[String], f: Value[String]): Reference[T] = Reference(k.scope(f))
35 | }
36 |
37 | implicit def list[T <: Primitive]: Aux[List[T], List[T]] = new Field[List[T]] {
38 | override type Container = List[T]
39 | override def apply(k: Variable[String], f: Value[String]): List[T] = List(k.scope(f))
40 | }
41 |
42 | implicit def set[T <: Primitive]: Aux[Set[T], Set[T]] = new Field[Set[T]] {
43 | override type Container = Set[T]
44 | override def apply(k: Variable[String], f: Value[String]): Set[T] = Set(k.scope[Int](f))
45 | }
46 |
47 | implicit def map[A <: String, B <: Primitive]: Aux[Map[A, B], Map[A, B]] = new Field[Map[A, B]] {
48 | override type Container = Map[A, B]
49 | override def apply(k: Variable[String], f: Value[String]): Map[A, B] = Map(k.scope[Int](f))
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/record/Reference.scala:
--------------------------------------------------------------------------------
1 | package caustic.library
2 | package typing
3 | package record
4 |
5 | import caustic.library.typing.Value._
6 |
7 | import shapeless._
8 | import shapeless.ops.hlist.LeftFolder
9 | import shapeless.ops.record.{Keys, Selector}
10 |
11 | /**
12 | * A record reference.
13 | *
14 | * @param pointer Underlying pointer.
15 | */
16 | case class Reference[T](pointer: Variable[String]) extends Internal {
17 |
18 | /**
19 | * Returns the address of the reference.
20 | *
21 | * @return Referenced address.
22 | */
23 | def key: Value[String] = this.pointer.get
24 |
25 | /**
26 | * Returns a container that stores the value of the attribute with the specified name.
27 | *
28 | * @param witness Attribute name.
29 | * @param gen Generic representation.
30 | * @param selector Attribute selector.
31 | * @param field Field converter.
32 | * @return Container for the value of the field.
33 | */
34 | def get[Repr <: HList, Name <: Symbol, Type, Container](witness: Witness.Lt[Name])(
35 | implicit gen: LabelledGeneric.Aux[T, Repr],
36 | selector: Selector.Aux[Repr, Name, Type],
37 | field: Field.Aux[Type, Container]
38 | ): Container = field(this.pointer, witness.value.name)
39 |
40 | /**
41 | * Deletes all attributes of the record.
42 | *
43 | * @param recursive Recursively delete referenced records.
44 | * @param context Parse context.
45 | * @param generic Generic representation.
46 | * @param keys Attribute names.
47 | * @param folder Attribute iterator.
48 | */
49 | def delete[Repr <: HList, KeysRepr <: HList](recursive: scala.Boolean = false)(
50 | implicit context: Context,
51 | generic: LabelledGeneric.Aux[T, Repr],
52 | keys: Keys.Aux[Repr, KeysRepr],
53 | folder: LeftFolder.Aux[KeysRepr, ops.delete.Args[T], ops.delete.type, ops.delete.Args[T]]
54 | ): Unit = keys().foldLeft(ops.delete.Args(this, recursive))(ops.delete)
55 |
56 | /**
57 | * Copies all attributes of this record to the destination record.
58 | *
59 | * @param destination Destination record.
60 | * @param context Parse context.
61 | * @param generic Generic representation.
62 | * @param keys Attribute names.
63 | * @param folder Attribute iterator.
64 | */
65 | def move[Repr <: HList, KeysRepr <: HList](destination: Reference[T])(
66 | implicit context: Context,
67 | generic: LabelledGeneric.Aux[T, Repr],
68 | keys: Keys.Aux[Repr, KeysRepr],
69 | folder: LeftFolder.Aux[KeysRepr, ops.move.Args[T], ops.move.type, ops.move.Args[T]]
70 | ): Unit = keys().foldLeft(ops.move.Args(this, destination))(ops.move)
71 |
72 | /**
73 | * Returns whether or not the references are equal.
74 | *
75 | * @param that Another reference.
76 | * @param context Parse context.
77 | * @param generic Generic representation.
78 | * @param keys Attribute names.
79 | * @param folder Attribute iterator.
80 | * @return Whether or not the references are equal.
81 | */
82 | def ===[Repr <: HList, KeysRepr <: HList](that: Reference[T])(
83 | implicit context: Context,
84 | generic: LabelledGeneric.Aux[T, Repr],
85 | keys: Keys.Aux[Repr, KeysRepr],
86 | folder: LeftFolder.Aux[KeysRepr, ops.equal.Args[T], ops.equal.type, ops.equal.Args[T]]
87 | ): Value[Boolean] = keys().foldLeft(ops.equal.Args(this, that, false))(ops.equal).equals
88 |
89 | /**
90 | * Serializes all attributes of the record to JSON.
91 | *
92 | * @param context Parse context.
93 | * @param generic Generic representation.
94 | * @param keys Attribute names.
95 | * @param folder Attribute iterator.
96 | */
97 | def asJson[Repr <: HList, KeysRepr <: HList](
98 | implicit context: Context,
99 | generic: LabelledGeneric.Aux[T, Repr],
100 | keys: Keys.Aux[Repr, KeysRepr],
101 | folder: LeftFolder.Aux[KeysRepr, ops.json.Args[T], ops.json.type, ops.json.Args[T]]
102 | ): Value[String] = {
103 | val json = convert("{\"key\": ") + this.pointer.key.quoted
104 | json + keys().foldLeft(ops.json.Args(this, ""))(ops.json).json + "}"
105 | }
106 |
107 | }
108 |
109 | object Reference {
110 |
111 | /**
112 | * Returns a reference to the specified local variable.
113 | *
114 | * @param key Local variable.
115 | * @return
116 | */
117 | def Local[T](key: Value[String]): Reference[T] = Reference(Variable.Local(key))
118 |
119 | /**
120 | * Returns a reference to the specified remote variable.
121 | *
122 | * @param key Remote variable.
123 | * @return Remote reference.
124 | */
125 | def Remote[T](key: Value[String]): Reference[T] = Reference(Variable.Remote(key))
126 |
127 | // Implicit Operations.
128 | implicit class AssignmentOps[T](x: Reference[T]) {
129 | def :=[U](y: Pointer[U])(implicit context: Context): Unit = x.pointer.set(y.key)
130 | def :=[Repr <: HList, KeysRepr <: HList](y: Reference[T])(
131 | implicit context: Context,
132 | generic: LabelledGeneric.Aux[T, Repr],
133 | keys: Keys.Aux[Repr, KeysRepr],
134 | folder: LeftFolder.Aux[KeysRepr, ops.move.Args[T], ops.move.type, ops.move.Args[T]]
135 | ): Unit = if (x.pointer.getClass == y.pointer.getClass) x.pointer := y.pointer else y.move(x)
136 | }
137 |
138 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/record/ops/delete.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 | package record
3 | package ops
4 |
5 | import caustic.library.Context
6 | import caustic.library.typing.collection._
7 | import caustic.runtime.Null
8 |
9 | import shapeless._
10 | import shapeless.ops.hlist.LeftFolder
11 | import shapeless.ops.record.{Keys, Selector}
12 |
13 | object delete extends Poly2 {
14 |
15 | /**
16 | * A delete argument.
17 | *
18 | * @param src Source reference.
19 | * @param recursive Recursively delete referenced records.
20 | */
21 | case class Args[T](src: Reference[T], recursive: scala.Boolean)
22 |
23 | implicit def caseValue[T,
24 | TRepr <: HList,
25 | FieldName <: Symbol,
26 | FieldType,
27 | FieldT <: Primitive
28 | ](
29 | implicit context: Context,
30 | generic: LabelledGeneric.Aux[T, TRepr],
31 | selector: Selector.Aux[TRepr, FieldName, FieldType],
32 | field: Field.Aux[FieldType, Variable[FieldT]],
33 | evidence: FieldType <:< Value[FieldT]
34 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
35 | field(x.src, f.name) := Null
36 | x
37 | }
38 |
39 | implicit def caseReference[
40 | T,
41 | TRepr <: HList,
42 | FieldName <: Symbol,
43 | FieldType,
44 | FieldT,
45 | FieldRepr <: HList,
46 | FieldKeys <: HList
47 | ](
48 | implicit context: Context,
49 | generic: LabelledGeneric.Aux[T, TRepr],
50 | selector: Selector.Aux[TRepr, FieldName, FieldType],
51 | field: Field.Aux[FieldType, Reference[FieldT]],
52 | fieldGeneric: LabelledGeneric.Aux[FieldT, FieldRepr],
53 | fieldKeys: Keys.Aux[FieldRepr, FieldKeys],
54 | fieldFolder: LeftFolder.Aux[FieldKeys, Args[FieldT], delete.type, Args[FieldT]],
55 | evidence: FieldType <:< Reference[FieldT]
56 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
57 | if (x.recursive) field(x.src, f.name).delete(x.recursive)
58 | x.src.pointer := Null
59 | x
60 | }
61 |
62 | implicit def caseRecord[
63 | T,
64 | TRepr <: HList,
65 | FieldName <: Symbol,
66 | FieldType,
67 | FieldT,
68 | FieldRepr <: HList,
69 | FieldKeys <: HList
70 | ](
71 | implicit context: Context,
72 | generic: LabelledGeneric.Aux[T, TRepr],
73 | selector: Selector.Aux[TRepr, FieldName, FieldType],
74 | field: Field.Aux[FieldType, Reference[FieldT]],
75 | fieldGeneric: LabelledGeneric.Aux[FieldT, FieldRepr],
76 | fieldKeys: Keys.Aux[FieldRepr, FieldKeys],
77 | fieldFolder: LeftFolder.Aux[FieldKeys, Args[FieldT], delete.type, Args[FieldT]],
78 | evidence: FieldType <:!< Reference[FieldT]
79 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
80 | field(x.src, f.name).delete(x.recursive)
81 | x
82 | }
83 |
84 | implicit def caseList[
85 | T,
86 | TRepr <: HList,
87 | FieldName <: Symbol,
88 | FieldType,
89 | FieldT <: Primitive
90 | ](
91 | implicit context: Context,
92 | generic: LabelledGeneric.Aux[T, TRepr],
93 | selector: Selector.Aux[TRepr, FieldName, FieldType],
94 | field: Field.Aux[FieldType, List[FieldT]],
95 | evidence: FieldType <:< List[FieldT]
96 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
97 | field(x.src, f.name).delete()
98 | x
99 | }
100 |
101 | implicit def caseSet[
102 | T,
103 | TRepr <: HList,
104 | FieldName <: Symbol,
105 | FieldType,
106 | FieldT <: Primitive
107 | ](
108 | implicit context: Context,
109 | generic: LabelledGeneric.Aux[T, TRepr],
110 | selector: Selector.Aux[TRepr, FieldName, FieldType],
111 | field: Field.Aux[FieldType, Set[FieldT]],
112 | evidence: FieldType <:< Set[FieldT]
113 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
114 | field(x.src, f.name).delete()
115 | x
116 | }
117 |
118 | implicit def caseMap[
119 | T,
120 | TRepr <: HList,
121 | FieldName <: Symbol,
122 | FieldType,
123 | FieldKey <: String,
124 | FieldValue <: Primitive
125 | ](
126 | implicit context: Context,
127 | generic: LabelledGeneric.Aux[T, TRepr],
128 | selector: Selector.Aux[TRepr, FieldName, FieldType],
129 | field: Field.Aux[FieldType, Map[FieldKey, FieldValue]],
130 | evidence: FieldType <:< Map[FieldKey, FieldValue]
131 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
132 | field(x.src, f.name).delete()
133 | x
134 | }
135 |
136 | }
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/record/ops/equal.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 | package record
3 | package ops
4 |
5 | import caustic.library.Context
6 | import caustic.library.typing.collection._
7 | import caustic.library.typing.Value._
8 |
9 | import shapeless._
10 | import shapeless.ops.hlist.LeftFolder
11 | import shapeless.ops.record.{Keys, Selector}
12 |
13 | object equal extends Poly2 {
14 |
15 | /**
16 | * An equal argument.
17 | *
18 | * @param a A reference.
19 | * @param b Another reference.
20 | */
21 | case class Args[T](a: Reference[T], b: Reference[T], equals: Value[Boolean])
22 |
23 | implicit def caseValue[T,
24 | TRepr <: HList,
25 | FieldName <: Symbol,
26 | FieldType,
27 | FieldT <: Primitive
28 | ](
29 | implicit context: Context,
30 | generic: LabelledGeneric.Aux[T, TRepr],
31 | selector: Selector.Aux[TRepr, FieldName, FieldType],
32 | field: Field.Aux[FieldType, Variable[FieldT]],
33 | evidence: FieldType <:< Value[FieldT]
34 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
35 | x.copy(equals = x.equals && field(x.a, f.name) === field(x.b, f.name))
36 | }
37 |
38 | implicit def caseReference[
39 | T,
40 | TRepr <: HList,
41 | FieldName <: Symbol,
42 | FieldType,
43 | FieldT,
44 | FieldRepr <: HList,
45 | FieldKeys <: HList
46 | ](
47 | implicit context: Context,
48 | generic: LabelledGeneric.Aux[T, TRepr],
49 | selector: Selector.Aux[TRepr, FieldName, FieldType],
50 | field: Field.Aux[FieldType, Reference[FieldT]],
51 | fieldGeneric: LabelledGeneric.Aux[FieldT, FieldRepr],
52 | fieldKeys: Keys.Aux[FieldRepr, FieldKeys],
53 | fieldFolder: LeftFolder.Aux[FieldKeys, Args[FieldT], equal.type, Args[FieldT]],
54 | evidence: FieldType <:< Reference[FieldT]
55 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
56 | x.copy(equals = x.equals && field(x.a, f.name).pointer === field(x.b, f.name).pointer)
57 | }
58 |
59 | implicit def caseRecord[
60 | T,
61 | TRepr <: HList,
62 | FieldName <: Symbol,
63 | FieldType,
64 | FieldT,
65 | FieldRepr <: HList,
66 | FieldKeys <: HList
67 | ](
68 | implicit context: Context,
69 | generic: LabelledGeneric.Aux[T, TRepr],
70 | selector: Selector.Aux[TRepr, FieldName, FieldType],
71 | field: Field.Aux[FieldType, Reference[FieldT]],
72 | fieldGeneric: LabelledGeneric.Aux[FieldT, FieldRepr],
73 | fieldKeys: Keys.Aux[FieldRepr, FieldKeys],
74 | fieldFolder: LeftFolder.Aux[FieldKeys, Args[FieldT], equal.type, Args[FieldT]],
75 | evidence: FieldType <:!< Reference[FieldT]
76 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
77 | x.copy(equals = x.equals && field(x.a, f.name) === field(x.b, f.name))
78 | }
79 |
80 | implicit def caseList[
81 | T,
82 | TRepr <: HList,
83 | FieldName <: Symbol,
84 | FieldType,
85 | FieldValue <: Primitive
86 | ](
87 | implicit context: Context,
88 | generic: LabelledGeneric.Aux[T, TRepr],
89 | selector: Selector.Aux[TRepr, FieldName, FieldType],
90 | field: Field.Aux[FieldType, List[FieldValue]],
91 | evidence: FieldType <:< List[FieldValue]
92 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
93 | x.copy(equals = x.equals && field(x.a, f.name) === field(x.b, f.name))
94 | }
95 |
96 | implicit def caseSet[
97 | T,
98 | TRepr <: HList,
99 | FieldName <: Symbol,
100 | FieldType,
101 | FieldValue <: Primitive
102 | ](
103 | implicit context: Context,
104 | generic: LabelledGeneric.Aux[T, TRepr],
105 | selector: Selector.Aux[TRepr, FieldName, FieldType],
106 | field: Field.Aux[FieldType, Set[FieldValue]],
107 | evidence: FieldType <:< Set[FieldValue]
108 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
109 | x.copy(equals = x.equals && field(x.a, f.name) === field(x.b, f.name))
110 | }
111 |
112 | implicit def caseMap[
113 | T,
114 | TRepr <: HList,
115 | FieldName <: Symbol,
116 | FieldType,
117 | FieldKey <: String,
118 | FieldValue <: Primitive
119 | ](
120 | implicit context: Context,
121 | generic: LabelledGeneric.Aux[T, TRepr],
122 | selector: Selector.Aux[TRepr, FieldName, FieldType],
123 | field: Field.Aux[FieldType, Map[FieldKey, FieldValue]],
124 | evidence: FieldType <:< Map[FieldKey, FieldValue]
125 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
126 | x.copy(equals = x.equals && field(x.a, f.name) === field(x.b, f.name))
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/caustic-library/src/main/scala/caustic/library/typing/record/ops/move.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing
2 | package record
3 | package ops
4 |
5 | import caustic.library.Context
6 | import caustic.library.typing.collection._
7 |
8 | import shapeless._
9 | import shapeless.ops.hlist.LeftFolder
10 | import shapeless.ops.record.{Keys, Selector}
11 |
12 | object move extends Poly2 {
13 |
14 | /**
15 | * A move argument.
16 | *
17 | * @param src Source record.
18 | * @param dest Destination record.
19 | */
20 | case class Args[T](src: Reference[T], dest: Reference[T])
21 |
22 | implicit def caseValue[
23 | T,
24 | TRepr <: HList,
25 | FieldName <: Symbol,
26 | FieldType,
27 | FieldT <: Primitive
28 | ](
29 | implicit context: Context,
30 | generic: LabelledGeneric.Aux[T, TRepr],
31 | selector: Selector.Aux[TRepr, FieldName, FieldType],
32 | field: Field.Aux[FieldType, Variable[FieldT]],
33 | evidence: FieldType <:< Value[FieldT]
34 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
35 | field(x.dest, f.name) := field(x.src, f.name)
36 | x
37 | }
38 |
39 | implicit def caseReference[
40 | T,
41 | TRepr <: HList,
42 | FieldName <: Symbol,
43 | FieldType,
44 | FieldT,
45 | FieldRepr <: HList,
46 | FieldKeys <: HList
47 | ](
48 | implicit context: Context,
49 | generic: LabelledGeneric.Aux[T, TRepr],
50 | selector: Selector.Aux[TRepr, FieldName, FieldType],
51 | field: Field.Aux[FieldType, Reference[FieldT]],
52 | evidence: FieldType <:< Reference[FieldT]
53 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
54 | field(x.dest, f.name).pointer := field(x.src, f.name).pointer
55 | x
56 | }
57 |
58 | implicit def caseList[
59 | T,
60 | TRepr <: HList,
61 | FieldName <: Symbol,
62 | FieldType,
63 | FieldValue <: Primitive
64 | ](
65 | implicit context: Context,
66 | generic: LabelledGeneric.Aux[T, TRepr],
67 | selector: Selector.Aux[TRepr, FieldName, FieldType],
68 | field: Field.Aux[FieldType, List[FieldValue]],
69 | evidence: FieldType <:< List[FieldValue]
70 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
71 | field(x.dest, f.name) := field(x.src, f.name)
72 | x
73 | }
74 |
75 | implicit def caseSet[
76 | T,
77 | TRepr <: HList,
78 | FieldName <: Symbol,
79 | FieldType,
80 | FieldValue <: Primitive
81 | ](
82 | implicit context: Context,
83 | generic: LabelledGeneric.Aux[T, TRepr],
84 | selector: Selector.Aux[TRepr, FieldName, FieldType],
85 | field: Field.Aux[FieldType, Set[FieldValue]],
86 | evidence: FieldType <:< Set[FieldValue]
87 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
88 | field(x.dest, f.name) := field(x.src, f.name)
89 | x
90 | }
91 |
92 | implicit def caseMap[
93 | T,
94 | TRepr <: HList,
95 | FieldName <: Symbol,
96 | FieldType,
97 | FieldKey <: String,
98 | FieldValue <: Primitive
99 | ](
100 | implicit context: Context,
101 | generic: LabelledGeneric.Aux[T, TRepr],
102 | selector: Selector.Aux[TRepr, FieldName, FieldType],
103 | field: Field.Aux[FieldType, Map[FieldKey, FieldValue]],
104 | evidence: FieldType <:< Map[FieldKey, FieldValue]
105 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
106 | field(x.dest, f.name) := field(x.src, f.name)
107 | x
108 | }
109 |
110 | implicit def caseRecord[
111 | T,
112 | TRepr <: HList,
113 | FieldName <: Symbol,
114 | FieldType,
115 | FieldT,
116 | FieldRepr <: HList,
117 | FieldKeys <: HList
118 | ](
119 | implicit context: Context,
120 | generic: LabelledGeneric.Aux[T, TRepr],
121 | selector: Selector.Aux[TRepr, FieldName, FieldType],
122 | field: Field.Aux[FieldType, Reference[FieldT]],
123 | fieldGeneric: LabelledGeneric.Aux[FieldT, FieldRepr],
124 | fieldKeys: Keys.Aux[FieldRepr, FieldKeys],
125 | fieldFolder: LeftFolder.Aux[FieldKeys, Args[FieldT], move.type, Args[FieldT]],
126 | evidence: FieldType <:!< Reference[FieldT]
127 | ): Case.Aux[Args[T], FieldName, Args[T]] = at[Args[T], FieldName] { (x, f) =>
128 | field(x.src, f.name).move(field(x.dest, f.name))
129 | x
130 | }
131 |
132 | }
--------------------------------------------------------------------------------
/caustic-library/src/test/scala/BUILD:
--------------------------------------------------------------------------------
1 | junit_tests(
2 | name='scala',
3 | sources=rglobs('*.scala'),
4 | dependencies=[
5 | 'caustic-library/src/main/scala',
6 | '3rdparty/jvm:junit',
7 | '3rdparty/jvm:mockito',
8 | '3rdparty/jvm:scalatest',
9 | ],
10 | )
11 |
--------------------------------------------------------------------------------
/caustic-library/src/test/scala/caustic/library/math/PackageTest.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.math
2 |
3 | import caustic.library.typing._
4 | import caustic.library.typing.Value._
5 |
6 | import org.junit.runner.RunWith
7 | import org.scalatest.junit.JUnitRunner
8 | import org.scalatest.{FunSuite, Matchers}
9 |
10 | @RunWith(classOf[JUnitRunner])
11 | class PackageTest extends FunSuite with Matchers {
12 |
13 | test("Expressions are simplified") {
14 | // Absolute values.
15 | abs(-1) === 1 shouldBe True
16 | abs(+1) === 1 shouldBe True
17 | abs(+0) === 0 shouldBe True
18 |
19 | // Inverse trigonometric functions.
20 | asin(+0.5) >= Pi / 6 - 0.01 shouldBe True
21 | asin(+0.5) <= Pi / 6 + 0.01 shouldBe True
22 | asin(-0.5) >= - Pi / 6 - 0.01 shouldBe True
23 | asin(-0.5) <= - Pi / 6 + 0.01 shouldBe True
24 |
25 | // Rounding.
26 | round(0.5) === 1 shouldBe True
27 | round(0.4) === 0 shouldBe True
28 | round(0.55, 0.1) === 0.6 shouldBe True
29 | round(0.52, 0.1) === 0.5 shouldBe True
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/caustic-library/src/test/scala/caustic/library/typing/record/ReferenceTest.scala:
--------------------------------------------------------------------------------
1 | package caustic.library.typing.record
2 |
3 | import caustic.library.typing.{Value, _}
4 | import org.junit.runner.RunWith
5 | import org.scalatest.junit.JUnitRunner
6 | import org.scalatest.{FunSuite, Matchers}
7 |
8 | @RunWith(classOf[JUnitRunner])
9 | class ReferenceTest extends FunSuite with Matchers {
10 |
11 | case class Foo(
12 | x: Value[Int],
13 | y: Reference[Bar],
14 | z: Bar
15 | )
16 |
17 | case class Bar(
18 | a: String
19 | )
20 |
21 | test("References are statically typed") {
22 | val foo = Reference[Foo](Variable.Local("foo"))
23 | "foo.get('a)" shouldNot compile
24 | "foo.get('x)" should compile
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/caustic-runtime/src/main/scala/BUILD:
--------------------------------------------------------------------------------
1 | scala_library(
2 | name='scala',
3 | sources=rglobs('*.scala'),
4 | dependencies=[
5 | '3rdparty/jvm:beaker-client',
6 | ],
7 | provides=artifact(
8 | org='com.madavan',
9 | name='caustic-runtime_2.12',
10 | repo=public,
11 | publication_metadata=describe,
12 | ),
13 | )
14 |
--------------------------------------------------------------------------------
/caustic-runtime/src/main/scala/caustic/runtime/Volume.scala:
--------------------------------------------------------------------------------
1 | package caustic.runtime
2 |
3 | import beaker.client._
4 | import beaker.server.protobuf._
5 |
6 | import scala.collection.concurrent
7 | import scala.util.Try
8 |
9 | /**
10 | * A transactional key-value store. Thread-safe.
11 | */
12 | trait Volume extends Serializable {
13 |
14 | /**
15 | * Returns the revisions of the specified keys.
16 | *
17 | * @param keys Keys to retrieve.
18 | * @return Revisions of keys.
19 | */
20 | def get(keys: Set[Key]): Try[Map[Key, Revision]]
21 |
22 | /**
23 | * Conditionally applies the changes if the dependencies remain unchanged.
24 | *
25 | * @param depends Dependencies.
26 | * @param changes Conditional updates.
27 | * @return Whether or not the changes were applied.
28 | */
29 | def cas(depends: Map[Key, Version], changes: Map[Key, Value]): Try[Unit]
30 |
31 | }
32 |
33 | object Volume {
34 |
35 | /**
36 | * An in-memory, thread-safe database. Useful for testing.
37 | *
38 | * @param map Underlying map.
39 | */
40 | class Memory(map: concurrent.Map[Key, Revision]) extends Volume {
41 |
42 | override def get(keys: Set[Key]): Try[Map[Key, Revision]] = {
43 | Try(keys.map(k => k -> this.map.getOrElse(k, Revision.defaultInstance)).toMap)
44 | }
45 |
46 | override def cas(depends: Map[Key, Version], changes: Map[Key, Value]): Try[Unit] = synchronized {
47 | Try(depends.keySet.map(k => k -> this.map.getOrElse(k, Revision.defaultInstance)).toMap)
48 | .filter(_ forall { case (k, v) => depends(k) >= v.version })
49 | .map(_ => changes map { case (k, v) => k -> Revision(depends(k) + 1, v) })
50 | .map(this.map ++= _)
51 | }
52 |
53 | }
54 |
55 | object Memory {
56 |
57 | /**
58 | * Constructs an empty in-memory database.
59 | *
60 | * @return Empty local database.
61 | */
62 | def empty: Volume.Memory =
63 | Volume.Memory(Map.empty[Key, Revision])
64 |
65 | /**
66 | * Constructs an in-memory database initialized with the specified values.
67 | *
68 | * @param initial Initial values.
69 | * @return Initialized local database.
70 | */
71 | def apply(initial: (Key, Revision)*): Volume.Memory =
72 | Volume.Memory(initial.toMap)
73 |
74 | /**
75 | * Constructs an in-memory database initialized with the specified values.
76 | *
77 | * @param initial Initialized values.
78 | * @return Initialized local database.
79 | */
80 | def apply(initial: Map[Key, Revision]): Volume.Memory =
81 | new Volume.Memory(concurrent.TrieMap(initial.toSeq: _*))
82 |
83 | }
84 |
85 | /**
86 | * A Beaker database.
87 | *
88 | * @param client Beaker client.
89 | */
90 | class Beaker(client: Client) extends Volume {
91 |
92 | override def get(keys: Set[Key]): Try[Map[Key, Revision]] =
93 | this.client.get(keys)
94 |
95 | override def cas(depends: Map[Key, Version], changes: Map[Key, Value]): Try[Unit] =
96 | this.client.cas(depends, changes).map(_ => ())
97 |
98 | }
99 |
100 | object Beaker {
101 |
102 | /**
103 | * Constructs a Beaker database connected to the specified address.
104 | *
105 | * @param name Hostname.
106 | * @param port Port number.
107 | * @return Connected beaker database.
108 | */
109 | def apply(name: String, port: Int): Volume.Beaker =
110 | Volume.Beaker(Address(name, port))
111 |
112 | /**
113 | * Constructs a Beaker database connected to the specified address.
114 | *
115 | * @param address Network location.
116 | * @return Connected beaker database.
117 | */
118 | def apply(address: Address): Volume.Beaker =
119 | Volume.Beaker(Client(address))
120 |
121 | /**
122 | * Constructs a Beaker database connected to the specified client.
123 | *
124 | * @param client Beaker client.
125 | * @return Connected beaker database.
126 | */
127 | def apply(client: Client): Volume.Beaker =
128 | new Volume.Beaker(client)
129 |
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/caustic-runtime/src/main/scala/caustic/runtime/operator.scala:
--------------------------------------------------------------------------------
1 | package caustic.runtime
2 |
3 | /**
4 | * An enumeration of the various transformations permitted in an expressions.
5 | */
6 | sealed trait Operator
7 | case object Add extends Operator // Returns (1) plus (2).
8 | case object Both extends Operator // Returns (1) && (2).
9 | case object Branch extends Operator // Evaluate (2) if (1) or (3) otherwise.
10 | case object Cons extends Operator // Evaluate (1) and then (2).
11 | case object Contains extends Operator // Returns whether (2) is contained in (1).
12 | case object Cos extends Operator // Returns the cosine of (1) in radians.
13 | case object Div extends Operator // Returns (1) divided by (2).
14 | case object Either extends Operator // Returns (1) || (2).
15 | case object Equal extends Operator // Returns whether (1) equals (2).
16 | case object Floor extends Operator // Returns the floor of (1).
17 | case object IndexOf extends Operator // Returns the index of (2) in (1).
18 | case object Length extends Operator // Returns the string length of (1).
19 | case object Less extends Operator // Returns whether (1) is less than (2).
20 | case object Load extends Operator // Returns the value of variable (1).
21 | case object Log extends Operator // Returns the natural logarithm of (1).
22 | case object Matches extends Operator // Returns whether (1) matches regex (2).
23 | case object Mod extends Operator // Retu rns (1) mod (2).
24 | case object Mul extends Operator // Returns (1) times (2).
25 | case object Negate extends Operator // Returns the negation of (1).
26 | case object Pow extends Operator // Returns (1) raised to the (2).
27 | case object Prefetch extends Operator // Reads keys at (1)/i for i in [0, (2)).
28 | case object Read extends Operator // Returns the value of key (1).
29 | case object Repeat extends Operator // Evaluate (2) until (1) is not satisfied.
30 | case object Rollback extends Operator // Discard changes and return (1).
31 | case object Sin extends Operator // Returns the sine of (1) in radians.
32 | case object Slice extends Operator // Returns the substring of (1) in [2, 3).
33 | case object Store extends Operator // Sets the value of variable (1) to (2).
34 | case object Sub extends Operator // Returns (1) minus (2).
35 | case object Write extends Operator // Sets the value of key (1) to (2).
--------------------------------------------------------------------------------
/caustic-runtime/src/main/scala/caustic/runtime/package.scala:
--------------------------------------------------------------------------------
1 | package caustic
2 |
3 | import scala.language.implicitConversions
4 |
5 | package object runtime extends Builder {
6 |
7 | // Implicit Conversions.
8 | implicit def bol2flag(value: Boolean): Flag = if (value) True else False
9 | implicit def num2real[T](value: T)(implicit num: Numeric[T]): Real = Real(num.toDouble(value))
10 | implicit def str2text(value: String): Text = if (value.isEmpty) Empty else Text(value)
11 |
12 | }
--------------------------------------------------------------------------------
/caustic-runtime/src/main/scala/caustic/runtime/program.scala:
--------------------------------------------------------------------------------
1 | package caustic.runtime
2 |
3 | import caustic.runtime.Runtime.Fault
4 |
5 | import java.io._
6 | import scala.util.Try
7 |
8 | /**
9 | * A procedure. Programs are abstract-syntax trees composed of literals and expressions.
10 | */
11 | sealed trait Program
12 |
13 | /**
14 | * A literal transformation. Expressions map literal operands to a literal result using their
15 | * operator. Because all expressions return literal values on literal arguments, every program can
16 | * eventually be reduced to a single literal value.
17 | *
18 | * @param operator Transformation.
19 | * @param operands Arguments.
20 | */
21 | case class Expression(operator: Operator, operands: List[Program]) extends Program
22 |
23 | /**
24 | * A typed value. Literals contain primitive values of type null, flag, real, or text which
25 | * correspond to null, boolean, double, and string respectively.
26 | */
27 | sealed trait Literal extends Program with Serializable
28 | case object Null extends Literal
29 | case class Flag(value: Boolean) extends Literal
30 | case class Real(value: Double) extends Literal
31 | case class Text(value: String) extends Literal
32 |
33 | object Literal {
34 |
35 | /**
36 | * Constructs a literal from the serialized binary representation.
37 | *
38 | * @param binary Serialized representation.
39 | * @return Literal.
40 | */
41 | def apply(binary: String): Literal = binary match {
42 | case x if x.isEmpty => Null
43 | case x if x(0) == 'f' => if (x(1) == '1') flag(true) else flag(false)
44 | case x if x(0) == 'r' => Real(x.substring(1).toDouble)
45 | case x if x(0) == 't' => Text(x.substring(1))
46 | }
47 |
48 | // Implicit Operations.
49 | implicit class SerializationOps(x: Literal) {
50 |
51 | def asBinary: String = x match {
52 | case Null => ""
53 | case Flag(t) => if (t) "f1" else "f0"
54 | case Real(t) => "r" + t
55 | case Text(t) => "t" + t
56 | }
57 |
58 | def asString: String = x match {
59 | case Null => "null"
60 | case Flag(a) => a.toString
61 | case Real(a) => if (a == math.floor(a)) a.toInt.toString else a.toString
62 | case Text(a) => a
63 | }
64 |
65 | def asDouble: Double = x match {
66 | case Null => 0
67 | case Flag(a) => if (a) 1 else 0
68 | case Real(a) => a
69 | case Text(a) => Try(a.toDouble).getOrElse(throw Fault(s"Unable to convert $a to double"))
70 | }
71 |
72 | def asInt: Int = x match {
73 | case Null => 0
74 | case Flag(a) => if (a) 1 else 0
75 | case Real(a) => a.toInt
76 | case Text(a) => Try(a.toInt).getOrElse(throw Fault(s"Unable to convert $a to int"))
77 | }
78 |
79 | def asBoolean: Boolean = x match {
80 | case Null => false
81 | case Flag(a) => a
82 | case Real(a) => a != 0
83 | case Text(a) => a.nonEmpty
84 | }
85 |
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/caustic-runtime/src/test/scala/BUILD:
--------------------------------------------------------------------------------
1 | junit_tests(
2 | name='scala',
3 | sources=rglobs('*.scala'),
4 | dependencies=[
5 | 'caustic-runtime/src/main/scala',
6 | '3rdparty/jvm:junit',
7 | '3rdparty/jvm:mockito',
8 | '3rdparty/jvm:scalatest',
9 | ],
10 | )
11 |
--------------------------------------------------------------------------------
/caustic-runtime/src/test/scala/caustic/runtime/BuilderTest.scala:
--------------------------------------------------------------------------------
1 | package caustic.runtime
2 |
3 | import caustic.runtime
4 |
5 | import org.junit.runner.RunWith
6 | import org.scalatest.junit.JUnitRunner
7 | import org.scalatest.{FunSuite, Matchers}
8 |
9 | @RunWith(classOf[JUnitRunner])
10 | class BuilderTest extends FunSuite with Matchers {
11 |
12 | test("Literals are cached") {
13 | flag(true) should be theSameInstanceAs flag(true)
14 | flag(false) should be theSameInstanceAs flag(false)
15 | text("") should be theSameInstanceAs text("")
16 | }
17 |
18 | test("Expressions are simplified") {
19 | // Math Expressions.
20 | add(6, 9) shouldEqual real(15)
21 | add("a", "b") shouldEqual text("ab")
22 | add(true, false) shouldEqual real(1)
23 | add("a", 0) shouldEqual text("a0")
24 | add("a", true) shouldEqual text("atrue")
25 | add(3.2, true) shouldEqual real(4.2)
26 | sub(9, 6) shouldEqual real(3)
27 | mul(2, 3) shouldEqual real(6)
28 | div(5, 2) shouldEqual real(2.5)
29 | mod(5, 2) shouldEqual real(1)
30 | pow(5, 2) shouldEqual real(25)
31 | log(math.exp(2)) shouldEqual real(2)
32 | sin(0) shouldEqual real(0)
33 | cos(0) shouldEqual real(1)
34 | floor(1) shouldEqual real(1)
35 | floor(1.5) shouldEqual real(1)
36 |
37 | // String Expressions.
38 | runtime.length("hello") shouldEqual real(5.0)
39 | slice("hello", 1, 3) shouldEqual text("el")
40 | matches("a41i3", "[a-z1-4]+") shouldEqual True
41 | matches("a41i3", "[a-z1-4]") shouldEqual False
42 | contains("abc", "bc") shouldEqual True
43 | contains("abc", "de") shouldEqual False
44 | indexOf("hello", "l") shouldEqual real(2)
45 |
46 | // Logical Expressions.
47 | both(False, True) shouldEqual False
48 | both(True, True) shouldEqual True
49 | either(True, False) shouldEqual True
50 | either(False, False) shouldEqual False
51 | negate(False) shouldEqual True
52 | negate(0) shouldEqual True
53 | negate(1) shouldEqual False
54 | negate("") shouldEqual True
55 | negate("foo") shouldEqual False
56 | runtime.equal(Null, Null) shouldEqual True
57 | runtime.equal(Null, 0) shouldEqual True
58 | runtime.equal(0, 0) shouldEqual True
59 | runtime.equal("a", "a") shouldEqual True
60 | runtime.equal("", 0) shouldEqual False
61 | runtime.equal("0", 0) shouldEqual True
62 | runtime.equal("0.0", 0) shouldEqual False
63 | runtime.equal(True, False) shouldEqual False
64 | less(2, 10) shouldEqual True
65 | less(-1, 1) shouldEqual True
66 | less("a", "ab") shouldEqual True
67 | less(False, True) shouldEqual True
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/caustic-runtime/src/test/scala/caustic/runtime/RuntimeTest.scala:
--------------------------------------------------------------------------------
1 | package caustic.runtime
2 |
3 | import beaker.server.protobuf.Revision
4 | import caustic.{runtime => caustic}
5 |
6 | import org.junit.runner.RunWith
7 | import org.mockito.Mockito._
8 | import org.mockito.internal.stubbing.answers.CallsRealMethods
9 | import org.mockito.invocation.InvocationOnMock
10 | import org.scalatest.{FunSuite, Matchers}
11 | import org.scalatest.junit.JUnitRunner
12 | import org.scalatest.concurrent.ScalaFutures
13 | import org.scalatest.mockito.MockitoSugar
14 | import org.scalatest.time._
15 |
16 | import java.util.concurrent.{CountDownLatch, TimeUnit}
17 | import java.util.concurrent.atomic.AtomicInteger
18 | import scala.concurrent.ExecutionContext.Implicits.global
19 | import scala.concurrent.Future
20 | import scala.language.postfixOps
21 | import scala.util.Success
22 |
23 | @RunWith(classOf[JUnitRunner])
24 | class RuntimeTest extends FunSuite with MockitoSugar with ScalaFutures with Matchers {
25 |
26 | implicit val defaultPatience: PatienceConfig = PatienceConfig(
27 | timeout = Span(5, Seconds),
28 | interval = Span(100, Millis)
29 | )
30 |
31 | test("Execute is strongly consistent.") {
32 | val runtime = Runtime(Volume.Memory())
33 |
34 | // Get on an unknown key returns a null value.
35 | runtime.execute(read("x")) shouldBe Success(Null)
36 |
37 | // Get on a inserted key returns the inserted value.
38 | runtime.execute(write("x", 1)) shouldBe Success(Null)
39 | runtime.execute(read("x")) shouldBe Success(real(1))
40 |
41 | // Get on a modified key within a transaction returns the modified value.
42 | runtime.execute(cons(write("x", 2), read("x"))) shouldBe Success(real(2))
43 | }
44 |
45 | test("Execute is thread-safe.") {
46 | val runtime = Runtime(Volume.Memory())
47 |
48 | // Construct a transaction that increments a counter.
49 | val inc = write("x", add(1, branch(caustic.equal(read("x"), Null), 0, read("x"))))
50 |
51 | // Concurrently execute the transaction and count the total successes.
52 | val total = new AtomicInteger(0)
53 | val tasks = Seq.fill(8)(new Thread(() =>
54 | Seq.fill(10000)(runtime.execute(inc).foreach(_ => total.getAndIncrement()))
55 | ))
56 |
57 | // Verify that the number of increments matches the number of successful transactions.
58 | tasks.foreach(_.start())
59 | tasks.foreach(_.join())
60 | runtime.execute(read("x")) shouldBe Success(real(total.get()))
61 | }
62 |
63 | test("Execute maintains mutable state.") {
64 | val runtime = Runtime(Volume.Memory())
65 |
66 | // Verifies that local variables are correctly updated.
67 | runtime execute {
68 | cons(
69 | store("i", 0),
70 | cons(repeat(less(load("i"), 3), store("i", add(load("i"), 1))), load("i"))
71 | )
72 | } shouldBe Success(real(3))
73 | }
74 |
75 | test("Execute detects conflicts.") {
76 | // Spy the underlying database to insert latches inside its put method that will enable
77 | // deterministic injection of race conditions into transaction execution.
78 | val volume = spy(Volume.Memory())
79 | val runtime = Runtime(volume)
80 | val ready = new CountDownLatch(1)
81 | val block = new CountDownLatch(1)
82 |
83 | doAnswer(new CallsRealMethods {
84 | override def answer(invocation: InvocationOnMock): Object = {
85 | ready.countDown()
86 | assert(block.await(1, TimeUnit.SECONDS))
87 | super.answer(invocation)
88 | }
89 | }).when(volume).cas(Map("x" -> 0L), Map.empty)
90 |
91 | // Construct a read-only transaction and wait for it to reach the faked 'put' method. Then
92 | // construct a second transaction that updates the value of the field and unblocks the first
93 | // transaction after it completes execution. This introduces a conflict that should cause the
94 | // first transaction to fail.
95 | val exec = Future(runtime.execute(read("x")).get)
96 | assert(ready.await(100, TimeUnit.MILLISECONDS))
97 | runtime.execute(write("x", "foo")) shouldBe Success(Null)
98 | block.countDown()
99 |
100 | // Verify that the first transaction fails, but the second succeeds.
101 | whenReady(exec.failed)(_ shouldBe an [Exception])
102 | runtime.execute(read("x")) shouldBe Success(text("foo"))
103 | }
104 |
105 | test("Execute performs prefetching.") {
106 | val volume = spy(Volume.Memory(Map("x" -> Revision(1L, text("a").asBinary))))
107 | val runtime = Runtime(volume)
108 |
109 | runtime execute {
110 | cons(
111 | // Delay execution by three iterations.
112 | read(read(read("y"))),
113 | cons(
114 | // Branch prediction.
115 | branch(less(read("x"), "aa"), write("y", 1), read("z")),
116 | // Prefetched reads.
117 | read(add("a", read("x")))
118 | )
119 | )
120 | }
121 |
122 | // Verify that prefetching works correctly.
123 | verify(volume).get(Set("x", "y", "z"))
124 | verify(volume).get(Set("aa"))
125 | verify(volume).cas(Map("x" -> 1L, "y" -> 0L, "aa" -> 0L), Map("y" -> real(1).asBinary))
126 | verifyNoMoreInteractions(volume)
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/pants:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
3 | # Licensed under the Apache License, Version 2.0 (see LICENSE).
4 |
5 | # =============================== NOTE ===============================
6 | # This pants bootstrap script comes from the pantsbuild/setup
7 | # project and is intended to be checked into your code repository so
8 | # that any developer can check out your code and be building it with
9 | # pants with no prior setup needed.
10 | #
11 | # You can learn more here: https://pantsbuild.github.io/setup
12 | # ====================================================================
13 |
14 | set -e
15 |
16 | PYTHON=${PYTHON:-$(which python2.7)}
17 |
18 | PANTS_HOME="${PANTS_HOME:-${HOME}/.cache/pants/setup}"
19 | PANTS_BOOTSTRAP="${PANTS_HOME}/bootstrap-$(uname -s)-$(uname -m)"
20 |
21 | VENV_VERSION=15.0.2
22 |
23 | # This requirement is installed before pantsbuild.pants to hack around
24 | # interpreters that have newer setuptools already installed, effectively
25 | # re-installing an older setuptools and pinning low to a version known to be
26 | # ingestable by both pants and pex for all reasonable versions of pants.
27 | # See:
28 | # https://github.com/pantsbuild/pants/issues/3948
29 | # https://github.com/pantsbuild/setup/issues/14
30 | # https://github.com/pantsbuild/setup/issues/19
31 | SETUPTOOLS_REQUIREMENT="setuptools==5.4.1"
32 |
33 | VENV_PACKAGE=virtualenv-${VENV_VERSION}
34 | VENV_TARBALL=${VENV_PACKAGE}.tar.gz
35 |
36 | # The high-level flow:
37 | # 1.) Grab pants version from pants.ini or default to latest.
38 | # 2.) Check for a venv via a naming/path convention and execute if found.
39 | # 3.) Otherwise create venv and re-exec self.
40 | #
41 | # After that pants itself will handle making sure any requested plugins
42 | # are installed and up to date.
43 |
44 | function tempdir {
45 | mktemp -d "$1"/pants.XXXXXX
46 | }
47 |
48 | # TODO(John Sirois): GC race loser tmp dirs leftover from bootstrap_XXX
49 | # functions. Any tmp dir w/o a symlink pointing to it can go.
50 |
51 | function bootstrap_venv {
52 | if [[ ! -d "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}" ]]
53 | then
54 | (
55 | mkdir -p "${PANTS_BOOTSTRAP}" && \
56 | staging_dir=$(tempdir "${PANTS_BOOTSTRAP}") && \
57 | cd "${staging_dir}" && \
58 | curl -LO https://pypi.io/packages/source/v/virtualenv/${VENV_TARBALL} && \
59 | tar -xzf ${VENV_TARBALL} && \
60 | ln -s "${staging_dir}/${VENV_PACKAGE}" "${staging_dir}/latest" && \
61 | mv "${staging_dir}/latest" "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}"
62 | ) 1>&2
63 | fi
64 | echo "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}"
65 | }
66 |
67 | function bootstrap_pants {
68 | pants_requirement="pantsbuild.pants"
69 | pants_version=$(
70 | grep -E "^[[:space:]]*pants_version" pants.ini 2>/dev/null | \
71 | cut -f2 -d: | tr -d " "
72 | )
73 | if [[ -n "${pants_version}" ]]
74 | then
75 | pants_requirement="${pants_requirement}==${pants_version}"
76 | else
77 | pants_version="unspecified"
78 | fi
79 |
80 | if [[ ! -d "${PANTS_BOOTSTRAP}/${pants_version}" ]]
81 | then
82 | (
83 | # NB: We setup the virtualenv with no setuptools to ensure our
84 | # ${SETUPTOOLS_REQUIREMENT} wins.
85 | venv_path="$(bootstrap_venv)" && \
86 | staging_dir=$(tempdir "${PANTS_BOOTSTRAP}") && \
87 | "${PYTHON}" "${venv_path}/virtualenv.py" --no-setuptools --no-download \
88 | "${staging_dir}/install" && \
89 | "${staging_dir}/install/bin/python" \
90 | "${staging_dir}/install/bin/pip" install \
91 | "${SETUPTOOLS_REQUIREMENT}" && \
92 | "${staging_dir}/install/bin/python" \
93 | "${staging_dir}/install/bin/pip" install \
94 | "${pants_requirement}" && \
95 | ln -s "${staging_dir}/install" "${staging_dir}/${pants_version}" && \
96 | mv "${staging_dir}/${pants_version}" "${PANTS_BOOTSTRAP}/${pants_version}"
97 | ) 1>&2
98 | fi
99 | echo "${PANTS_BOOTSTRAP}/${pants_version}"
100 | }
101 | pants_dir=$(bootstrap_pants) && \
102 | exec "${pants_dir}/bin/python" "${pants_dir}/bin/pants" "$@"
103 |
--------------------------------------------------------------------------------
/pants.ini:
--------------------------------------------------------------------------------
1 | [GLOBAL]
2 | pants_version: 1.3.0
3 | pythonpath: [
4 | 'build-support/pants',
5 | ]
6 | backend_packages: +[
7 | 'publish',
8 | 'caustic.pants',
9 | ]
10 | pants_ignore: +[
11 | '/build-support/*.venv/',
12 | ]
13 | plugins: +[
14 | 'caustic.pants',
15 | ]
16 |
17 | [binaries]
18 | path_by_id: {('darwin', '17'): ('mac', '10.12')}
19 |
20 | [compile.zinc]
21 | args: [
22 | '-C-encoding',
23 | '-CUTF-8',
24 | '-S-encoding',
25 | '-SUTF-8',
26 | '-S-g:vars',
27 | '-S-Ypatmat-exhaust-depth',
28 | '-Soff',
29 | '-S-feature',
30 | '-S-Ywarn-unused:-implicits',
31 | ]
32 | jvm_options: +[
33 | '-Xss1g',
34 | ]
35 |
36 | [doc.javadoc]
37 | # Enables javadoc generation for generated sources.
38 | include_codegen: True
39 |
40 | [ivy]
41 | # Fetches PGP signing libraries for Maven artifacts.
42 | ivy_profile: %(pants_supportdir)s/ivy/pgp.ivysettings.xml
43 |
44 | [jvm-distributions]
45 | minimum_version: 1.8.0
46 | maximum_version: 1.8.999
47 |
48 | [jvm-platform]
49 | default_platform: java8
50 | platforms: {
51 | 'java6': {'source': '6', 'target': '6', 'args': [] },
52 | 'java7': {'source': '7', 'target': '7', 'args': [] },
53 | 'java8': {'source': '8', 'target': '8', 'args': [] },
54 | }
55 |
56 | [lint.scalafmt]
57 | skip: True
58 |
59 | [publish.jar]
60 | force: True
61 | ivy_settings: %(pants_supportdir)s/ivy/publish.ivysettings.xml
62 | repos: {
63 | 'public': {
64 | 'resolver': 'oss.sonatype.org',
65 | 'auth': 'build-support/ivy:netrc',
66 | 'help': 'Configure your ~/.netrc for oss.sonatype.org access.'
67 | },
68 | }
69 | restrict_push_branches: [
70 | 'master',
71 | ]
72 | restrict_push_urls: [
73 | 'git@github.com:ashwin153/caustic.git',
74 | 'https://github.com/ashwin153/caustic.git'
75 | ]
76 |
77 | [repl.scala]
78 | main: ammonite.Main
79 |
80 | [scala-platform]
81 | version: custom
82 | suffix_version: 2.12
83 |
84 | [thrift-binary]
85 | version: 0.10.0
86 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ####################################################################################################
3 | # Declare Build Targets and Artifacts #
4 | # https://github.com/ashwin153/caustic/wiki/Release #
5 | ####################################################################################################
6 | targets=(
7 | "caustic-compiler/src/main/antlr"
8 | "caustic-compiler/src/main/scala"
9 | "caustic-library/src/main/scala"
10 | "caustic-runtime/src/main/scala"
11 | )
12 |
13 | artifacts=(
14 | "caustic-grammar"
15 | "caustic-compiler_2.12"
16 | "caustic-library_2.12"
17 | "caustic-runtime_2.12"
18 | )
19 |
20 | ####################################################################################################
21 | # Publish Artifacts to Maven Central #
22 | # DO NOT MODIFY #
23 | ####################################################################################################
24 | branch=$(git symbolic-ref --short HEAD)
25 | if [ -n "$(git status --porcelain)" ]; then
26 | echo -e "Current branch \033[0;33m$branch\033[0m has uncommitted changes."
27 | exit 1
28 | else
29 | read -p "Artifact version (defaults to incrementing patch version): " version
30 | read -r -p "$(echo -e -n "Confirm release of \033[0;33m$branch\033[0;0m? [y|N] ")" response
31 | fi
32 |
33 | if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]] ; then
34 | if [ -z "$version" ] ; then
35 | publish="./pants publish.jar --publish-jar-no-dryrun ${targets[@]}"
36 | else
37 | overrides=("${artifacts[@]/#/--publish-jar-override=com.madavan#}")
38 | overrides=("${overrides[@]/%/=$version}")
39 | publish="./pants publish.jar --publish-jar-no-dryrun ${overrides[@]} ${targets[@]}"
40 | fi
41 |
42 | if eval "./pants test ::" && eval $publish ; then
43 | /usr/bin/open -a "/Applications/Google Chrome.app" \
44 | 'http://www.pantsbuild.org/release_jvm.html#promoting-to-maven-central'
45 | /usr/bin/open -a "/Applications/Google Chrome.app" \
46 | 'https://oss.sonatype.org/#stagingRepositories'
47 | fi
48 | fi
49 |
--------------------------------------------------------------------------------