Giter Club home page Giter Club logo

claro-lang's Introduction

Visit docs.clarolang.com for Comprehensive User Docs

Get your first Claro project setup, and go through a tutorial walking you through everything you need to know to start building your own Claro programs!

Learn about all of Claro's features with full examples and explanations to help you learn about the tools at your disposal.

View the APIs of the various Modules currently available in Claro's StdLib.

Learn Claro By Example!

Check out the example Claro programs.

You may be interested in checking out some solutions to this year's Advent of Code written in Claro!

Syntax Highlighting

Syntax highlighting support is available for Vim, VSCode and IntelliJ users. Instructions for getting setup are available under tools/syntax_highlighting/.

claro-lang's People

Contributors

ar2202 avatar jasonsteving99 avatar jzwood avatar kmayhue avatar swork1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

claro-lang's Issues

Add Support for Command Line Flags at Language Level

Needs both research into implementing this with a good flags library, and also blocked by Javac Multi Files https://github.com/JasonSteving99/claro-lang/projects/1#card-59558893

Claro's going to be used in production.. let's just acknowledge that now and build in common sense things that are gonna be needed in any production system. Let's start with flags.

e.g.:

final var ENV: string = Flags.string("env", "flag description");
final var OPTIONAL_BEHAVIOR: optional = Flags.optionalInt("optional_behavior", "flag description");

Model an Actual Value for Types (including Type Aliases)

Currently there's no way of representing a Type as a first order value in Claro. In order to support higher order metaprogramming we need to at least start here. For now, I'd be happy with this just involving adding the intermediaterepresentation/ types to generated output as values that can be passed around. This implies that a new meta BaseType.TYPE will be needed.

Implement String interpolation

We should be able to create complex formatted strings much easier than a bunch of concatenations in a loop... Let's make something more akin to Google's Soy templates.

Figure Out Javac Deps for JavaSource Compiler Backend

Right now the JavaSource Compiler Backend can only depend on Java std library imports or on code that's hardcoded into the source in ProgramNode. This is terrible because certain things like Guava and even the Types.java file that the compiler depends on would be essential to continue expanding the JavaSource backend.

This task is to figure out how to get the genrule that runs javac over the generated Claro javasource intermediate file to depend on other Java source files that will act as Claro's std library.

Support Compile-Time-Constant evaluation.

Claro should, at compile-time, evaluate all Expressions as far as possible without input needed at runtime. For example, at the most basic level Claro should be evaluating all constant arithmetic Expressions and replacing them with a constant so that this isn't delayed until runtime.

E.g.
at compile time:
var x = 5 + 4;
should be converted to:
var x = 9;

More importantly however, this is primarily done in order to establish all Types at runtime as much as possible. Particularly for the case of the Tuple-subscript Expr.

E.g.

var t: tuple<int, int, string> = (1, 2, "hello");

# Must be cast to int, and would be valid since indices 0&1 are both integers.
var x = (int) t[randomChoice(0,  1)];

# Shouldn't have to be cast since `0` is a compile-time-constant integer expression. 
var y = t[3 - 2]; # or t[1]

Implement Multi-Line Strings

It's often super annoying to create large multi-line strings in languages like Java that rely on concatenation to achieve this. I'd like something like:

var s = 
"""
This is some long multi-line string
that's spread
on many lines
""";
print(s);

printing that thing should result in multi-line output.

Implement `eval("...")` in the StdLib

I'll be honest....I think that this will be where Claro jumps the shark and I'm pretty likely to consider remove its support before ever actually trying to release Claro as something that I think real people should be using... but in the meantime it'll be seriously dope to see this in action dog-fooding the interpreted compiler backend within the StdLib of the JAVA_SOURCE backend, and even somewhat recursively through the REPL backend!

It'd be cool to be able to do something like:

eval("
var s = input(\"hey gimme some input\");
print(s);
");

Implement Providers

Procedures that don't take any args but yet return a value will be known as Providers in Claro.

provider foo() -> int {
return 10;
}

Implement Some Sort of `inject` Semantics

Currently Claro's dep injection only works by somewhat abusing scoping rules to allow injected procedures to be called directly from within a using-block because the procedure was defined in the same file. However, looking forward to the future when Claro supports multiple files, Claro's dep injection would actually not be extensible to calling procedures defined in other files even if bindings are provided for them in a module, because their names will not be present in scope. This applies to non-procedure bindings as well. Claro should support an inject keyword to direct Claro to provide the bound instance indicated by the modules in use.

Maybe can look something like:

using (FooModule) {
  # The type annotation may be omitted from the left side.
  var myVar = inject foo:FooType;
}

This would also allow Claro to be lazy and only initialize the bindings that are explicitly requested for injection. This would be a very useful optimization to reduce the cost of using a Module, so that the performance penalty of using a Module doesn't silently grow as bindings are added to the Module unless more bindings from the module are actually injected (directly or indirectly).

Implement Consumers

Procedures that do take args but yet don't return a value will be known as Consumers in Claro. Basically these should necessarily have a side-effect.

consumer foo(consumed: int) {
print(consumed);
}

Cleanup .../intermediate_representation/ Dir

This is in dire need of refactoring so that it's not just one massive directory containing every file in the world. We should at least refactor to nest all files by super-class type e.g.:

  • intermediate_representation/
    • expressions/
      • *Expr.java
    • statements/
      • *Stmt.java
    • terms/
      • *Term.java
        ...

Implement Pattern Matching

I really don't feel that Exceptions serve to enhance the understandability of a program's flow of control. So I don't want to build Claro around exceptions. Instead, I think procedures should return error values in results so that programmers can directly check for success. This leads to some super gross code without pattern matching, so for this reason alone I find enough justification to implement pattern matching. As an incredible bonus, Claro will also get the incredible expressivity that pattern matching gives a language in the general case, not even considering error handling.

Should look something like Rust syntax: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html

Make All Defined Functions Available w/o Regard to Definition Order

Currently functions are only accessible on lines following their declaration. Somehow add another new preprocessing compiler phase (which runs before the current type-checking phase) that finds all functions so that they're defined before type-checking happens regardless of what order the calls come in the linear source (and honestly it should store this signature metadata somewhere outside the current ScopedHeap because we might also want to track this signature info for things like named params).

Implement the Trashcan Operator

It's getting a bit old to have to have every single piece of data end up eventually ending up in a print statement due to the fact that all variables must be referenced and that functions/providers cannot be called without referencing their output.

I want a trashcan operator to allow things in the language to go unused, while still being super explicit that they're not used, and also being clear that it's a waste to have things around without using them.

Should look like this:

_ = foo(0);  # throw away the result of this function call.

Syntax for list comprehensions

Hi @JasonSteving99, awesome work!

I'm reading the reference guide carefully and I find Claro a nice and pleasant language.

I love the pipe operator (that I first met with Elm) and, as you explain here it allows program text and data to flow in the same direction (thus its easier to read than the pipes-free version)
(BTW, I found the ^ back-reference operator very clean and clear)

That property of program text and data flowing in the same direction is somewhat broken in the case of list comprehensions because identifiers are used before they are defined. For example, in

[strings::repeated("*", x) | x in [1, 3, 99, 2, 1] where x <= 10]

x is referenced (in strings::repeated("*", x)) before its definition (as x in [1, 3, 99, 2, 1] where x <= 10)

In short and simple list comprehensions, as the above one, the syntax might not be an obstacle for understanding but in longer comprehensions understanding is hindered. For example, in the following code:

files::readOrPanic(resources::Input)
|> strings::split(^, "\n")
|> [
[
ints::parseInt(strings::fromChar(x))
| x in strings::splitChars(line) where ints::parseInt(strings::fromChar(x)) instanceof int
]
| line in ^
]

The reader needs to go until line 9 to find the definition of line and then go back to line 7 where line is used to define x that is first referenced at line 6.

I know the roots of the syntax for list comprehensions is the set builder notation but IMO that syntax is OK for simple cases (as is the case of all the examples provided in the description of the notation) but for more complex cases the syntax that reverts the order use-def into def-use is more clear. There are languages, like Scala, with a syntax like that.

I think something like

files::readOrPanic(resources::Input)
  |> strings::split(^, "\n")
  |> [ line in ^ ->
      [ x in strings::splitChars(line) where ints::parseInt(strings::fromChar(x)) instanceof int ->
        ints::parseInt(strings::fromChar(x))
      ]
     ]

reads better than

files::readOrPanic(resources::Input)
|> strings::split(^, "\n")
|> [
[
ints::parseInt(strings::fromChar(x))
| x in strings::splitChars(line) where ints::parseInt(strings::fromChar(x)) instanceof int
]
| line in ^
]

What do you think?

Implicit `Optional<T>` Procedure Return Semantics

Claro should support Optional<T> at the language level. While it makes for a really safe programming pattern, the use of Optional<T> throughout a codebase often makes for some very verbose code. Claro should support a language-level semantic for procedures returning optional things. In the case that a Procedure declares an Optional return type, we should allow programmers to omit things like return Optional.empty(); or return Optional.of(foo); instead they should be able to simply use return; or return foo; respectively. This should be a much less annoying approach to implementing Optional semantics in the language without introducing null.

We should be able to do something like the following:

function foo(x: int) -> optional<int> {
  if (...some condition where I *might* have an answer...) {
    if (...some condition where I realize I actually do not...) {
      return;
    }
    return 100;
  }
  # I still do not want to allow implicit returns by falling through the
  # end of the procedure body, so in this other case where you do not
  # have a value to return you still need a return but it can be empty.
  return;
}

Add Claro Syntax Highlighting to "GitHub Linguist"

Apparently GitHub implements its syntax highlighting based on rules in this file https://github.com/github/linguist/blob/master/lib/linguist/languages.yml

In order for .claro files to start getting properly syntax highlighted, I'd need to get a pull request approved on that repo....that's probably not going to happen for quite some time, particularly until Claro's syntax is much more stable and more importantly Claro is not so much a "toy language". So I'll consider this a finish line goal for a few years from now :).

Support One-Of Types for Undecidable Types That Programmer Can't Even Decide at Compile-Time

Basically in the situation:

var t = (1, "hello");
var x = t[randomChoice(0, 1)];

there's no way for compiler OR programmer to decide the type of the variable x at compile-time.

So I think we need some way to represent that situation in a type-safe manner. Let's try a new concept of OneOf Types and see what happens. (Btw, this would tie in well with a match stmt).

var t = (1, "hello");
var x: oneof<int | string> = t[randomChoice(0, 1)];
match type(x) {
  int    -> ...
  string -> ...
}

Target JVM Bytecode

Make a new JVM_BYTECODE backend. Get rid of the javac dep used by the JAVA_SOURCE backend.

Lambdas Should Not Be Able to Modify Values of Closure Captured Variables

Currently the following works in the REPL but fails in the JavaSource backend due to Java's checks that lambdas only reference "effectively final" variables.

var x = 10;
var c: consumer<int> = y -> { x = 99; print(y + x); };
print(x);   # Prints 10
c(0);         # Prints 99
print(x);   # Prints 198

However, this shouldn't work at all in either backend. Lambdas should simply be able to read the current state of closure variables (at the line that they were defined on), not write to them.

I think that I'll need to implement this by inspecting the Lambda body's stmt list to find which variables from the outer scope were referenced, and then adding a stmt prefix statement to store those variables' values in hidden variables before the lambda definition statement and then implicitly adds extra trailing arguments for those implicit closure captured variables, and passes them into a partial application lambda that takes just the closure captured variables and returns a function that takes just the original variables the user defined lambda was supposed to take, but which calls the actual generated lambda with the user's body stmts for the caller as a convenience. In this way, we're acknowledging that the lambda the user defined is actually one that implicitly captures a closure and returns a partial application.

The following Claro code:

var x = 10;
var c: consumer<int> = y -> { x = 99; print(y + x); };

would look something like this in Generated java source:

int x = 10;
final int $x = x;  // Capture closure vars.
... // define some wrapper classes
$$lambda1 $lambda1 = $$lambda1Closure.apply($x);

The reason we'd use this approach is because this way, we wouldn't need to modify any of the user's variable names within the function body stmt list in generated code, and we'd be able to treat Lambdas the same as all other Procedures are treated by the Claro compiler and actually just create a brand new ScopedHeap instance for them to operate on. Except, we'll manually populate that heap with closure captured variables. Also it's cool that we'll dogfood lambdas for this behavior.

Support Recursion in JAVA_SOURCE Backend

Currently recursion is only supported in the INTERPRETER backend. This is because in the java source impl the reference to the function is assumed to be the name of the identifier used for the call (since java doesn't really allow assigning functions to variables in this way). We'll need some way for the variable with the new name to track the original function name.

HttpService parsing issue

Compiler throws a syntax error when compiling the following example. The output error is misleading and doesn't point out that there is a trailing , in the HttpService definition.

Example

HttpService Server {
  rootEndpoint: "/",
}

endpoint_handlers Server {
  graph provider rootEndpoint() -> future<HttpResponse> {
    root res <- http::getOk200HttpResponseForHtml(@formattedHtml);
    node formattedHtml <- futures::immediateFuture("");
  }
}

var server: HttpServer<Server> = http::getBasicHttpServerForPort(8080);
http::startServerAndAwaitShutdown(server);

The compiler works fine if you remove the trailing , like so

HttpService Server {
  rootEndpoint: "/",
}

I attempted to look into ClaroParser.cup but I'm not familiar enough with how to possibly handle generating a more specific error message for http_endpoints_list. Curious as to what the solution would be!

Directions to run source code? Development workflow?

I was poking around in the source code and was trying to figure out how to run the compiler code given a .claro file. I am able to get an output file using a command like bazel-bin/bootstrapping_claro_compiler_binary --java_source --package com.swork.test --src /home/swork1/claro-lang-swork1/test.claro --classname Test --output_file_path src/java/com/swork/test/Test.java

The issue is I can't just run this .java file without the dependencies, how can I link the dependencies correctly? How you go about your development workflow in general? Is there a README I may be failing to find?

I've never used bazel before (spent a couple hours messing around now) so maybe I'm just missing something obvious here. Thanks!

Add Lambda Expressions

Let's add lambda expressions to the language mostly to provide some convenient way to make single-use procedures, but also to provide a convenient mechanism for implicit scope grabbing closures (or however you wanna describe that).

I'll work on the syntax later, but this lambda should have access to the variable defined before it:

var i = 99;
var foo: provider<int> = () -> i;
var bar: consumer<int> = (x) -> print(x + i);
var baz: function<int -> int> = (x) -> x + i;
print(foo()); 
bar();
print(baz(10));

Interop story

Hello, thanks for open sourcing claro - this looks quite interesting.

Coming from kotlin, I am curious what is your perspective on interop with other jvm libraries, which I couldn't glean from the docs. Is this something that will be possible and if so, will it require claro wrappers to be authored for individual libraries ? I guess the latter is needed to retain the guarantees claro provides but still curious where this lies in your roadmap.

Named Function Args

Should be able to do the following:

consumer foo(start: int, end: int, interval: int) {
    ...
}

foo(start = 0, end = 99, interval = 3);

Support Multiple Procedure Return Statements

Currently procedures only support a single return statement at the very end of the body, but we should support arbitrarily placed return statements to make control flow much easier.

Figure Out Claro Multi-files

Currently, Claro can only support a single file. I would like to be able to split Claro programs up into multiple files so I need to figure out some way to parse individual files (currently the compiled backend still actually reads from stdin under the hood) and to allow users to specify what they're importing.

Unable to Assign Oneof Variant to Field with Oneof Type

The following does not compile giving Invalid type: Found: LIST Expected: oneof<[int], int>

newtype Node : struct {
    example: oneof<[int], int>
}
var test = Node({example = [1,2]});
print(test);

Introducing a variable allows it to compile

var l = [1, 2];
var test = Node({example = l});

Is this intended?

Add Structs

Let's do this after functions because I want to be able to model struct constructors as any generic function

Claro needs structured value objects. These will hold data in named, typed fields and that's it. They should have a builder constructor automatically generated for them after definition.

E.g.:

struct Foo {
    x: int,
    y: string,
}

var f: Foo = Foo.builder().setX(10).setY("ten").build();

or maybe this will be a nicer builder syntax:

var f: Foo = Foo(x = 10, y = "ten");

Make `type(...)` Less Hacky

Currently the type(...) Statement is a complete hack and it simply prints out a string representation of the Claro type of an expression. Instead, it should actually be an Expression instead of a Statement and it should return a meta value which is of type Type. This would allow higher order programming allowing type "reflection".

This is currently done entirely at compile-time and I don't think that's what a user wants when they call type(...), they want runtime information. We need to make sure that all Claro builtins support the getClaroType() method, and at runtime we should be deferring to that method to determine the type.

We'll come up with some standardized way to reference non-Claro types later after native Java interop is supported. For now we should hunt down and kill all non-Claro-native types and replace them with a Claro equivalent in order to make runtime type checking a supported feature across all types at least until Java interop happens in some magical future.

Upgrade to Developing w/ Bazelisk

Currently I'm stuck developing locally on Bazel 3.7.2 and had to manually lock the GitHub Actions release to use that same Bazel release literally just because I've been too lazy to keep Bazel updated and now Bazel's on version 6+ and has made breaking changes. I'll need to upgrade to use Bazelisk locally so that I can ensure that I'm able to continue developing this project and can eventually have an easy onboarding path for anyone else that wants to get involved.

Grammar railroad diagram

Using a script doing a bit of regex search and repace and manually adding the tokens from the lexer we can get an EBNF understood by (IPV6) https://www.bottlecaps.de/rr/ui or (IPV4) https://rr.red-dove.com/ui to generate a nice navigable railroad diagram that can help document/develop/debug this project grammar.

Follow the instructions shown bellow at the top:

//
// EBNF to be viewd at
//	(IPV6) https://www.bottlecaps.de/rr/ui
//	(IPV4) https://rr.red-dove.com/ui
//
// Copy and paste this at one url shown above in the 'Edit Grammar' tab
// then click the 'View Diagram' tab.
//

// This is where the grammar starts.
program ::=
    stmts_and_defs_list

// TODO(steving) Add procedure defs to this target so that they also can only be used at the top level.
stmts_and_defs_list ::=
    stmt stmts_and_defs_list
  | module_definition_stmt stmts_and_defs_list
  | contract_definition_stmt stmts_and_defs_list
  | contract_implementation_stmt stmts_and_defs_list
  | generic_function_definition_stmt stmts_and_defs_list
  | generic_consumer_function_definition_stmt stmts_and_defs_list
  | generic_provider_function_definition_stmt stmts_and_defs_list
  // We'll allow files with only a single one of these following stmts as well.
  | stmt
  | module_definition_stmt
  | contract_definition_stmt
  | contract_implementation_stmt
  | generic_function_definition_stmt
  | generic_consumer_function_definition_stmt
  | generic_provider_function_definition_stmt

stmt_list ::=
    stmt stmt_list
  | stmt
  | debug_stmts

debug_stmts ::=
    DEBUG_DUMP_SCOPE LPAR RPAR SEMICOLON

stmt ::=
    print
  | show_type
  | identifier_declaration
  | identifier_assignment
  | trashcan_assignment
  | identifier_increment_stmt
  | identifier_decrement_stmt
  | struct_field_assignment_stmt
  | list_element_assignment
  | if_else_chain_stmt
  | match_stmt
  | while_stmt
  | for_loop_stmt
  | repeat_stmt
  | break_stmt
  | continue_stmt
  | function_definition_stmt
  | consumer_function_definition_stmt
  | provider_function_definition_stmt
  | consumer_function_call_stmt
  | graph_function_definition_stmt
  | graph_provider_definition_stmt
  | graph_consumer_definition_stmt
  | initializers_block_stmt
  | unwrappers_block_stmt
  | return_stmt
  | alias_stmt
  | atom_def_stmt
  | newtype_def_stmt
  | using_block_stmt
  | pipe_chain_stmt
  | http_service_def_stmt
  | endpoint_handlers_block_stmt
  | sleep
  | privileged_inline_java

module_definition_stmt ::=
    MODULE IDENTIFIER LCURLY bind_stmts_list RCURLY
  | MODULE IDENTIFIER USING LPAR identifier_list RPAR LCURLY bind_stmts_list RCURLY

bind_stmts_list ::=
    bind_stmt bind_stmts_list
  | bind_stmt

bind_stmt ::=
    BIND IDENTIFIER COLON builtin_type TO expr SEMICOLON

using_block_stmt ::=
    USING LPAR identifier_list RPAR LCURLY stmt_list RCURLY

print ::=
    PRINT LPAR expr RPAR SEMICOLON

show_type ::=
    TYPE LPAR expr RPAR SEMICOLON

unwrap_expr ::=
    UNWRAP LPAR expr RPAR

identifier_declaration ::=
    VAR IDENTIFIER ASSIGNMENT expr SEMICOLON
  | VAR IDENTIFIER COLON builtin_type SEMICOLON
  | VAR IDENTIFIER COLON builtin_type ASSIGNMENT expr SEMICOLON
  // Blocking variants.
  | VAR IDENTIFIER COLON builtin_type BLOCKING_GET expr SEMICOLON
  | VAR IDENTIFIER BLOCKING_GET expr SEMICOLON
  // Automatic error propagation variants.
  | VAR IDENTIFIER COLON builtin_type QUESTION_MARK_ASSIGNMENT expr SEMICOLON
  | VAR IDENTIFIER QUESTION_MARK_ASSIGNMENT expr SEMICOLON

alias_stmt ::=
    ALIAS IDENTIFIER COLON builtin_type

atom_def_stmt ::=
    ATOM identifier

newtype_def_stmt ::=
    NEWTYPE IDENTIFIER COLON builtin_type
  | NEWTYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET COLON builtin_type

initializers_block_stmt ::=
    INITIALIZERS IDENTIFIER LCURLY initializers_or_unwrappers_proc_defs_list RCURLY

unwrappers_block_stmt ::=
    UNWRAPPERS IDENTIFIER LCURLY initializers_or_unwrappers_proc_defs_list RCURLY

  // TODO(steving) Add support for Generic Graph Procedures when available.
initializers_or_unwrappers_proc_defs_list ::=
    function_definition_stmt initializers_or_unwrappers_proc_defs_list
  | provider_function_definition_stmt initializers_or_unwrappers_proc_defs_list
  | consumer_function_definition_stmt initializers_or_unwrappers_proc_defs_list
  | graph_function_definition_stmt initializers_or_unwrappers_proc_defs_list
  | graph_provider_definition_stmt initializers_or_unwrappers_proc_defs_list
  | generic_function_definition_stmt initializers_or_unwrappers_proc_defs_list
  | generic_provider_function_definition_stmt initializers_or_unwrappers_proc_defs_list
  | generic_consumer_function_definition_stmt initializers_or_unwrappers_proc_defs_list
  | function_definition_stmt
  | provider_function_definition_stmt
  | consumer_function_definition_stmt
  | graph_function_definition_stmt
  | graph_provider_definition_stmt
  | generic_function_definition_stmt
  | generic_provider_function_definition_stmt
  | generic_consumer_function_definition_stmt

builtin_type ::=
    base_builtin_type_without_mutability_modifier
  | MUT base_builtin_type_without_mutability_modifier

base_builtin_type_without_mutability_modifier ::=
    INT_TYPE
  | LONG_TYPE
  | FLOAT_TYPE
  | DOUBLE_TYPE
  | BOOLEAN_TYPE
  | STRING_TYPE
  | CHAR_TYPE
  | LBRACKET builtin_type RBRACKET
  | LCURLY builtin_type RCURLY
  | LCURLY builtin_type COLON builtin_type RCURLY
    // Maps are defined like {string} to avoid a new keyword `map` since I'd like to not impair functional style
    // where map is a well known function.
  | ONEOF L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET
  | FUNCTION_TYPE L_ANGLE_BRACKET builtin_type ARROW builtin_type R_ANGLE_BRACKET
  | FUNCTION_TYPE L_ANGLE_BRACKET BAR builtin_types_list BAR ARROW builtin_type R_ANGLE_BRACKET
  | BLOCKING FUNCTION_TYPE L_ANGLE_BRACKET builtin_type ARROW builtin_type R_ANGLE_BRACKET
  | BLOCKING FUNCTION_TYPE L_ANGLE_BRACKET BAR builtin_types_list BAR ARROW builtin_type R_ANGLE_BRACKET
  | CONSUMER_FUNCTION_TYPE L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET
  | BLOCKING CONSUMER_FUNCTION_TYPE L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET
  | PROVIDER_FUNCTION_TYPE L_ANGLE_BRACKET builtin_type R_ANGLE_BRACKET
  | BLOCKING PROVIDER_FUNCTION_TYPE L_ANGLE_BRACKET builtin_type R_ANGLE_BRACKET
  | TUPLE_TYPE L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET
  | FUTURE L_ANGLE_BRACKET builtin_type R_ANGLE_BRACKET
  | IDENTIFIER
  | SCOPED_IDENTIFIER
  | IDENTIFIER L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET
  | SCOPED_IDENTIFIER L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET
  | STRUCT_TYPE LCURLY function_args_types_list RCURLY
    // Structs are defined like `struct{field1: int ... fieldN: string}`. This is intended to resemble a sequence of
    // variable declarations which will align with the initialization syntax `{field1 = 1 ... fieldN = "foo"}` which
    // in turn is intended to resemble a sequence of variable initializations. I appreciate the metaphore of a struct as
    // a bundle of variables.
  | HTTP_RESPONSE
  | HTTP_CLIENT L_ANGLE_BRACKET builtin_type R_ANGLE_BRACKET
  | HTTP_SERVER L_ANGLE_BRACKET builtin_type R_ANGLE_BRACKET
  | SYNTHETIC_JAVA_TYPE LPAR STRING RPAR
  | SYNTHETIC_JAVA_TYPE L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET LPAR STRING RPAR

builtin_types_list ::=
    backwards_builtin_types_list

backwards_builtin_types_list ::=
    builtin_type COMMA backwards_builtin_types_list
  | builtin_type

identifier_assignment ::=
    identifier ASSIGNMENT expr SEMICOLON

trashcan_assignment ::=
    UNDERSCORE ASSIGNMENT expr SEMICOLON
  | UNDERSCORE QUESTION_MARK_ASSIGNMENT expr SEMICOLON

identifier_increment_stmt ::=
    identifier_increment SEMICOLON

identifier_increment ::=
    INCREMENT identifier
  | identifier INCREMENT

identifier_decrement_stmt ::=
    identifier_decrement SEMICOLON

identifier_decrement ::=
    DECREMENT identifier
  | identifier DECREMENT

list_element_assignment ::=
    collection_subscript ASSIGNMENT expr SEMICOLON
  | collection_subscript QUESTION_MARK_ASSIGNMENT expr SEMICOLON

list_remove_expr ::=
    REMOVE LPAR expr COMMA expr RPAR

// In order to support function calls w/in single-expr lambda bodies w/o making the grammar think that it has reached
// the end of the lambda at the first occurrence of `(` in the expr body I am making a separate grammar production for
// "all of the exprs including lambdas" and "all of the exprs not including lambdas" so that function application can be
// restricted to not work on lambdas right away. Solves the ambiguity in a minimally annoying way. To immediately call a
// lambda you'd need to wrap the lambda in parens or something.
expr ::=
    non_lambda_exprs
  | lambda_function_expr

non_lambda_exprs ::=
    MINUS expr
  | expr PLUS expr
  | identifier_increment
  | identifier_decrement
  | expr MINUS expr
  | expr MULTIPLY expr
  | expr DIVIDE expr
  | expr MODULUS expr
  | expr EXPONENTIATE expr
  | expr IN expr
  | parenthesized_expr
  | LOG_PREFIX float LPAR expr RPAR
  | LOG_PREFIX integer LPAR expr RPAR
  | NUMERIC_BOOL LPAR expr RPAR
  | input
  | list_remove_expr
  | unwrap_expr
  | is_input_ready
  | copy_expr
  | from_json_expr
  | list
  | map
  | list_comprehension_expr
  | set_comprehension_expr
  | map_comprehension_expr
  | collection_subscript
  | LEN LPAR expr RPAR
  | bool_expr
  | fmt_string
  | term
  | function_call_expr
  | typed_lambda_function_expr
  | provider_function_call_expr
  | tuple
  | set
  | struct
  | struct_field_access_expr
    // This is some unfortunate syntax for casting but this actually was necessary to avoid having casting create an
    // ambiguous grammar deciding whether `(i < len(l))` was a malformed cast (e.g. for some parameterized custom type
    // like `(MyType<foo>)`) or if it was actually a "less-than" operator within a parenthesized expression. From some
    // perspective this is an issue with JCUP being set to use too low of a lookahead....but I don't fully understand
    // the implications of trying to increase that so it'll stay this way.
  | CAST LPAR builtin_type COMMA expr RPAR
  | get_http_client_expr
  | get_basic_http_server_for_port_expr
  | error
    // TODO(steving) This is super helpful do the same thing for stmts.
    // There's been some syntax error in an expression somewhere.. meaning that this expression is getting thrown away
    // in order to continue checking for other errors in the input program. So "repair" the program by giving it some
    // arbitrary Expr that won't complain a second time during AST type checking.

parenthesized_expr ::=
    LPAR expr RPAR

sleep ::=
    SLEEP LPAR expr RPAR SEMICOLON

input ::=
    INPUT LPAR STRING RPAR
  | INPUT LPAR RPAR

is_input_ready ::=
    IS_INPUT_READY LPAR RPAR

copy_expr ::=
    COPY LPAR expr RPAR

from_json_expr ::=
    FROM_JSON LPAR expr RPAR

list ::=
    LBRACKET args_list RBRACKET
  | LBRACKET RBRACKET
  | MUT LBRACKET args_list RBRACKET
  | MUT LBRACKET RBRACKET

map ::=
    LCURLY RCURLY
  | LCURLY map_initializer_kv_list RCURLY
  | MUT LCURLY RCURLY
  | MUT LCURLY map_initializer_kv_list RCURLY

map_initializer_kv_list ::=
    expr COLON expr COMMA map_initializer_kv_list
  | expr COLON expr

tuple ::=
    // Tuples distinguish themselves from parenthesized expressions by having at least one comma.
    LPAR expr COMMA args_list RPAR
  | MUT LPAR expr COMMA args_list RPAR

set ::=
    // Sets distinguish themselves from parenthesized expressions by having at least one comma and tuples by {} instead of ().
    LCURLY expr COMMA args_list RCURLY
  | MUT LCURLY expr COMMA args_list RCURLY

args_list ::=
    expr COMMA args_list
  | expr

struct ::=
    LCURLY struct_field_initializers_list RCURLY
  | MUT LCURLY struct_field_initializers_list RCURLY

// Note that we're again building this up backwards for convenience (remember that all guava collections respect ordering).
struct_field_initializers_list ::=
    IDENTIFIER ASSIGNMENT expr COMMA struct_field_initializers_list
  | IDENTIFIER ASSIGNMENT expr

struct_field_access_expr ::=
    expr DOT IDENTIFIER

struct_field_assignment_stmt ::=
    struct_field_access_expr ASSIGNMENT expr SEMICOLON

collection_subscript ::=
    expr LBRACKET expr RBRACKET

bool_expr ::=
    equality
  | inequality
  | bool_arithmetic
  | instanceof_expr

// This calculator can evaluate an equality check of expressions.
equality ::=
    expr EQUALS expr
  | expr NOT_EQUALS expr

if_else_chain_stmt ::=
    if_stmt else_if_stmt_chain
  | if_stmt else_stmt
  | if_stmt

if_stmt ::=
    IF LPAR expr RPAR LCURLY stmt_list RCURLY

else_if_stmt_chain ::=
    else_if_stmt else_if_stmt_chain
  | else_if_stmt else_stmt
  | else_if_stmt

else_if_stmt ::=
    ELSE if_stmt

// We can simply return the StmtListNode itself because this is actually gonna be run by the IfStmt it's associated
// with.
else_stmt ::=
    ELSE LCURLY stmt_list RCURLY

match_stmt ::=
    MATCH LPAR expr RPAR LCURLY match_cases_list_stmt RCURLY

match_cases_list_stmt ::=
    CASE match_case_patterns ARROW stmt_list match_cases_list_stmt
  | CASE match_multi_expr_case ARROW stmt_list match_cases_list_stmt
  | CASE match_case_patterns ARROW stmt_list
  | CASE match_multi_expr_case ARROW stmt_list

match_case_patterns ::=
    primitive
  | MINUS INTEGER
  | identifier
  | identifier LPAR primitive RPAR
  | identifier LPAR match_case_patterns RPAR
  | scoped_identifier LPAR primitive RPAR
  | scoped_identifier LPAR match_case_patterns RPAR
  | LPAR match_case_patterns COMMA match_case_patterns_list RPAR
  | MUT LPAR match_case_patterns COMMA match_case_patterns_list RPAR
  | LCURLY match_case_pattern_struct_field_list RCURLY
  | MUT LCURLY match_case_pattern_struct_field_list RCURLY
  | UNDERSCORE COLON builtin_type
  | identifier COLON builtin_type
  | UNDERSCORE
    // TODO(steving) This should model `_` as an Expr to have something to point at in errors.

// TODO(steving) TESTING!! Make multi-expr-case support arbitrary patterns.
match_multi_expr_case ::=
    primitive BAR match_multi_expr_case
  | identifier LPAR primitive RPAR BAR match_multi_expr_case
  | primitive BAR primitive
  | identifier LPAR primitive RPAR BAR identifier LPAR primitive RPAR

match_case_patterns_list ::=
    match_case_patterns COMMA match_case_patterns_list
  | match_case_patterns

match_case_pattern_struct_field_list ::=
    identifier ASSIGNMENT match_case_patterns COMMA match_case_pattern_struct_field_list
  | identifier ASSIGNMENT match_case_patterns

while_stmt ::=
    WHILE LPAR expr RPAR LCURLY stmt_list RCURLY

for_loop_stmt ::=
    FOR LPAR identifier IN expr RPAR LCURLY stmt_list RCURLY

repeat_stmt ::=
    REPEAT LPAR expr RPAR LCURLY stmt_list RCURLY

break_stmt ::=
    BREAK SEMICOLON

continue_stmt ::=
    CONTINUE SEMICOLON

list_comprehension_expr ::=
    LBRACKET expr BAR identifier IN expr RBRACKET
  | MUT LBRACKET expr BAR identifier IN expr RBRACKET
  | LBRACKET expr BAR identifier IN expr WHERE expr RBRACKET
  | MUT LBRACKET expr BAR identifier IN expr WHERE expr RBRACKET

set_comprehension_expr ::=
    LCURLY expr BAR identifier IN expr RCURLY
  | MUT LCURLY expr BAR identifier IN expr RCURLY
  | LCURLY expr BAR identifier IN expr WHERE expr RCURLY
  | MUT LCURLY expr BAR identifier IN expr WHERE expr RCURLY

map_comprehension_expr ::=
    LCURLY expr COLON expr BAR identifier IN expr RCURLY
  | MUT LCURLY expr COLON expr BAR identifier IN expr RCURLY
  | LCURLY expr COLON expr BAR identifier IN expr WHERE expr RCURLY
  | MUT LCURLY expr COLON expr BAR identifier IN expr WHERE expr RCURLY

function_definition_stmt ::=
   FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | BLOCKING FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR BLOCKING FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | generic_blocking_on FUNCTION_TYPE IDENTIFIER LPAR procedure_args_w_generic_blocking RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR generic_blocking_on FUNCTION_TYPE IDENTIFIER LPAR procedure_args_w_generic_blocking RPAR ARROW builtin_type LCURLY stmt_list RCURLY

generic_function_definition_stmt ::=
   FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
 | REQUIRES LPAR required_contracts RPAR FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
 | BLOCKING FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
 | REQUIRES LPAR required_contracts RPAR BLOCKING FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | generic_blocking_on FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | REQUIRES LPAR required_contracts RPAR generic_blocking_on FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR ARROW builtin_type LCURLY stmt_list RCURLY

generic_blocking_on ::=
    BLOCKING COLON identifier_bar_sep_list

identifier_bar_sep_list ::=
    IDENTIFIER BAR identifier_bar_sep_list
  | IDENTIFIER

maybe_blocking_procedure_types ::=
    MAYBE_BLOCKING FUNCTION_TYPE L_ANGLE_BRACKET builtin_type ARROW builtin_type R_ANGLE_BRACKET
  | MAYBE_BLOCKING FUNCTION_TYPE L_ANGLE_BRACKET BAR builtin_types_list BAR ARROW builtin_type R_ANGLE_BRACKET
  | MAYBE_BLOCKING CONSUMER_FUNCTION_TYPE L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET
  | MAYBE_BLOCKING PROVIDER_FUNCTION_TYPE L_ANGLE_BRACKET builtin_type R_ANGLE_BRACKET

// TODO(steving) I need to enable generic functions to be generic over only a partial subset of the required
// TODO(steving) Contract's type params.
required_contracts ::=
    IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET COMMA required_contracts
  | scoped_identifier L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET COMMA required_contracts
  | IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET
  | scoped_identifier L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET

lambda_function_expr ::=
    IDENTIFIER ARROW expr
  | IDENTIFIER ARROW LCURLY stmt_list RCURLY
  | LAMBDA LPAR IDENTIFIER COMMA identifier_list RPAR ARROW expr
  | LAMBDA LPAR IDENTIFIER COMMA identifier_list RPAR ARROW LCURLY stmt_list RCURLY
  | LPAR RPAR ARROW expr
  | LPAR RPAR ARROW LCURLY stmt_list RCURLY

// TODO(steving) Lambda syntax has been complicated by parsing constraints. I don't actually want the `lambda` keyword
// TODO(steving) EVER. Figure out a way to actually define all lambda forms w/o ever using `lambda` keyword.
typed_lambda_function_expr ::=
    LPAR function_args_types_list RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | LAMBDA LPAR function_args_types_list RPAR ARROW LCURLY stmt_list RCURLY
  | LAMBDA LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY

identifier_list ::=
    IDENTIFIER COMMA identifier_list
  | IDENTIFIER

consumer_function_definition_stmt ::=
    CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
  | BLOCKING CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR BLOCKING CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
  | generic_blocking_on CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR procedure_args_w_generic_blocking RPAR LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR generic_blocking_on CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR procedure_args_w_generic_blocking RPAR LCURLY stmt_list RCURLY

generic_consumer_function_definition_stmt ::=
   CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
 | REQUIRES LPAR required_contracts RPAR CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
 | BLOCKING CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
 | REQUIRES LPAR required_contracts RPAR BLOCKING CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR LCURLY stmt_list RCURLY
  | generic_blocking_on CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR LCURLY stmt_list RCURLY
  | REQUIRES LPAR required_contracts RPAR generic_blocking_on CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR LCURLY stmt_list RCURLY

provider_function_definition_stmt ::=
    PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | BLOCKING PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | USING LPAR injected_keys_list RPAR BLOCKING PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY

generic_provider_function_definition_stmt ::=
    PROVIDER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | REQUIRES LPAR required_contracts RPAR PROVIDER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | BLOCKING PROVIDER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY
  | REQUIRES LPAR required_contracts RPAR BLOCKING PROVIDER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR RPAR ARROW builtin_type LCURLY stmt_list RCURLY

injected_keys_list ::=
    identifier COLON builtin_type COMMA injected_keys_list
  | identifier COLON builtin_type AS IDENTIFIER COMMA injected_keys_list
  | identifier COLON builtin_type
  | identifier COLON builtin_type AS IDENTIFIER

// TODO(steving) Be less lazy and don't bother using a Map where we should just be using a List of pairs....but Java is so garbage it doesn't have tuples so.....
// Note that we're again building this up backwards for convenience (remember that all guava collections respect ordering).
function_args_types_list ::=
    IDENTIFIER COLON builtin_type COMMA function_args_types_list
  | IDENTIFIER COLON builtin_type

// LITERALLY THE ONLY DIFFERENCE BETWEEN THIS AND THE ABOVE `function_args_types_list` production is that this one gives
// IdentifierReferenceTerms instead of Strings. Useful for logging error messages. Only really applicable to Graphs for
// now since we don't actually have restrictions on procedure args in general.
// TODO(steving) Also use this production for blocking generic args so that we can indicate when blocking generics were used incorrectly.
function_args_types_list_identifiers ::=
    identifier COLON builtin_type COMMA function_args_types_list_identifiers
  | identifier COLON builtin_type

procedure_args_w_generic_blocking ::=
    IDENTIFIER COLON maybe_blocking_procedure_types COMMA procedure_args_w_generic_blocking
  | IDENTIFIER COLON builtin_type COMMA procedure_args_w_generic_blocking
  | IDENTIFIER COLON maybe_blocking_procedure_types
  | IDENTIFIER COLON builtin_type

return_stmt ::=
    RETURN expr SEMICOLON

// Just to make sure that graphs are somewhat consistent you must either choose between putting the root node as the
// first or last node definition in the graph body. Let's encourage ordering these sanely because it can be hard to
// trace declarative code without a pattern.
graph_function_definition_stmt ::=
    GRAPH FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR ARROW builtin_type LCURLY root_node non_root_nodes_list RCURLY
  | GRAPH FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR ARROW builtin_type LCURLY non_root_nodes_list root_node RCURLY
  | USING LPAR injected_keys_list RPAR GRAPH FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR ARROW builtin_type LCURLY root_node non_root_nodes_list RCURLY
  | USING LPAR injected_keys_list RPAR GRAPH FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR ARROW builtin_type LCURLY non_root_nodes_list root_node RCURLY

// Just to make sure that graphs are somewhat consistent you must either choose between putting the root node as the
// first or last node definition in the graph body. Let's encourage ordering these sanely because it can be hard to
// trace declarative code without a pattern.
graph_provider_definition_stmt ::=
    GRAPH PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY root_node non_root_nodes_list RCURLY
  | GRAPH PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY non_root_nodes_list root_node RCURLY
  | USING LPAR injected_keys_list RPAR GRAPH PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY root_node non_root_nodes_list RCURLY
  | USING LPAR injected_keys_list RPAR GRAPH PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type LCURLY non_root_nodes_list root_node RCURLY

graph_consumer_definition_stmt ::=
    GRAPH CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR LCURLY root_node non_root_nodes_list RCURLY
  | GRAPH CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR LCURLY non_root_nodes_list root_node RCURLY
  | USING LPAR injected_keys_list RPAR GRAPH CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR LCURLY root_node non_root_nodes_list RCURLY
  | USING LPAR injected_keys_list RPAR GRAPH CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list_identifiers RPAR LCURLY non_root_nodes_list root_node RCURLY

root_node ::=
    ROOT IDENTIFIER LEFT_ARROW expr SEMICOLON

non_root_node ::=
    NODE IDENTIFIER LEFT_ARROW expr SEMICOLON

non_root_nodes_list ::=
    non_root_node non_root_nodes_list
  | non_root_node

node_reference ::=
    AT IDENTIFIER

contract_definition_stmt ::=
    CONTRACT IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LCURLY contract_signature_defs_list RCURLY
  | CONTRACT IDENTIFIER L_ANGLE_BRACKET identifier_list IMPLICATION_ARROW identifier_list R_ANGLE_BRACKET LCURLY contract_signature_defs_list RCURLY

contract_signature_defs_list ::=
    contract_procedure_signature_definition_stmt contract_signature_defs_list
  | contract_procedure_signature_definition_stmt

contract_procedure_signature_definition_stmt ::=
   // FUNCTIONS
   FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR ARROW builtin_type SEMICOLON
 | BLOCKING FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR ARROW builtin_type SEMICOLON
 | generic_blocking_on FUNCTION_TYPE IDENTIFIER LPAR procedure_args_w_generic_blocking RPAR ARROW builtin_type SEMICOLON
 | FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR ARROW builtin_type SEMICOLON
 | BLOCKING FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR ARROW builtin_type SEMICOLON
 | generic_blocking_on FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR ARROW builtin_type SEMICOLON
   // CONSUMERS
 | CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR SEMICOLON
 | BLOCKING CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR function_args_types_list RPAR SEMICOLON
 | generic_blocking_on CONSUMER_FUNCTION_TYPE IDENTIFIER LPAR procedure_args_w_generic_blocking RPAR SEMICOLON
 | CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR function_args_types_list RPAR SEMICOLON
 | BLOCKING CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR SEMICOLON
 | generic_blocking_on CONSUMER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR procedure_args_w_generic_blocking RPAR SEMICOLON
   // PROVIDERS
 | PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type SEMICOLON
 | BLOCKING PROVIDER_FUNCTION_TYPE IDENTIFIER LPAR RPAR ARROW builtin_type SEMICOLON
 | PROVIDER_FUNCTION_TYPE IDENTIFIER L_ANGLE_BRACKET identifier_list R_ANGLE_BRACKET LPAR RPAR ARROW builtin_type SEMICOLON

contract_implementation_stmt ::=
    IMPLEMENT IDENTIFIER L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET LCURLY contract_implementations_list RCURLY
  | IMPLEMENT scoped_identifier L_ANGLE_BRACKET builtin_types_list R_ANGLE_BRACKET LCURLY contract_implementations_list RCURLY

contract_implementations_list ::=
    function_definition_stmt contract_implementations_list
  | consumer_function_definition_stmt contract_implementations_list
  | provider_function_definition_stmt contract_implementations_list
  | generic_function_definition_stmt contract_implementations_list
  | generic_consumer_function_definition_stmt contract_implementations_list
  | function_definition_stmt
  | consumer_function_definition_stmt
  | provider_function_definition_stmt
  | generic_function_definition_stmt
  | generic_consumer_function_definition_stmt

pipe_chain_stmt ::=
    expr pipe_chain

pipe_chain ::=
    PIPE_ARROW expr pipe_chain
  | PIPE_ARROW consumer_function_call_stmt
  | PIPE_ARROW identifier_assignment
  | PIPE_ARROW identifier_declaration
  | PIPE_ARROW list_element_assignment
  | PIPE_ARROW print

function_call_expr ::=
    non_lambda_exprs LPAR args_list RPAR
  | scoped_identifier LPAR args_list RPAR
  // Foo:(arg1 ... argN)

provider_function_call_expr ::=
    non_lambda_exprs LPAR RPAR
  | scoped_identifier LPAR RPAR

consumer_function_call_stmt ::=
    non_lambda_exprs LPAR args_list RPAR SEMICOLON
  | scoped_identifier LPAR args_list RPAR SEMICOLON

inequality ::=
    expr L_ANGLE_BRACKET expr
  | expr R_ANGLE_BRACKET expr
  | expr LTE expr
  | expr GTE expr

bool_arithmetic ::=
    expr AND expr
  | expr OR expr
  | NOT expr

instanceof_expr ::=
    expr INSTANCEOF builtin_type

fmt_string ::=
    FMT_STRING_PART expr fmt_string
  | FMT_STRING_PART expr STRING

http_service_def_stmt ::=
    HTTP_SERVICE identifier LCURLY http_endpoints_list RCURLY

http_endpoints_list ::=
    identifier COLON fmt_string COMMA http_endpoints_list
  | identifier COLON STRING COMMA http_endpoints_list
  | identifier COLON fmt_string
  | identifier COLON STRING

endpoint_handlers_block_stmt ::=
    ENDPOINT_HANDLERS identifier LCURLY endpoint_handler_impl_graphs_list RCURLY

endpoint_handler_impl_graphs_list ::=
    graph_function_definition_stmt endpoint_handler_impl_graphs_list
  | graph_provider_definition_stmt endpoint_handler_impl_graphs_list
  | graph_function_definition_stmt
  | graph_provider_definition_stmt

get_http_client_expr ::=
    GET_HTTP_CLIENT LPAR expr RPAR

get_basic_http_server_for_port_expr ::=
    GET_BASIC_HTTP_SERVER_FOR_PORT LPAR expr RPAR

privileged_inline_java ::=
    PRIVILEGED_INLINE_JAVA

// The last production 'term' closes the grammar. It's a primitive or identifier reference.
term ::=
    primitive
  | identifier
  | scoped_identifier
  | node_reference
  | UP_ARROW

identifier ::=
    IDENTIFIER

scoped_identifier ::=
    SCOPED_IDENTIFIER

primitive ::=
    float
  | DOUBLE
  | integer
  | LONG
  | STRING
  | CHAR
  | TRUE
  | FALSE

float ::=
    FLOAT

integer ::=
    INTEGER

//Tokens
//\(\S+\)\s+{ return symbol(Tokens\.\([^, ]+\).*

/* Create a new parser symbol for the lexem. */
PLUS ::= "+"
INCREMENT ::= "++"
DECREMENT ::= "--"
MINUS ::= "-"
MULTIPLY ::= "*"
EXPONENTIATE ::= "**"
DIVIDE ::= "/"
MODULUS ::= "%"
LPAR ::= "("
RPAR ::= ")"
LCURLY ::= "{"
RCURLY ::= "}"
LBRACKET ::= "["
RBRACKET ::= "]"
EQUALS ::= "=="
NOT_EQUALS ::= "!="
L_ANGLE_BRACKET ::= "<"
R_ANGLE_BRACKET ::= ">"
LTE ::= "<="
GTE ::= ">="
OR ::= "or"
AND ::= "and"
NOT ::= "not"
ARROW ::= "->"
PIPE_ARROW ::= "|>"
IMPLICATION_ARROW ::= "=>"
TRUE ::= "true"
FALSE ::= "false"
VAR ::= "var"
ASSIGNMENT ::= "="
SEMICOLON ::= ";"
COLON ::= ":"
COMMA ::= ","
DOT ::= "."
BAR ::= "|"
IF ::= "if"
ELSE ::= "else"
MATCH ::= "match"
CASE ::= "case"
WHILE ::= "while"
FOR ::= "for"
REPEAT ::= "repeat"
BREAK ::= "break"
CONTINUE ::= "continue"
WHERE ::= "where"
RETURN ::= "return"
QUESTION_MARK_ASSIGNMENT ::= "?="

// Builtin functions are currently processed at the grammar level.. maybe there's a better generalized way.
LOG_PREFIX ::= "log_"
PRINT ::= "print"
NUMERIC_BOOL ::= "numeric_bool"
INPUT ::= "input"
IS_INPUT_READY ::= "isInputReady"
LEN ::= "len"
TYPE ::= "type"
REMOVE ::= "remove"
IN ::= "in"
INSTANCEOF ::= "instanceof"
COPY ::= "copy"
FROM_JSON ::= "fromJson"
SLEEP ::= "sleep"

// DEBUGGING keywords that should be removed when we want a real release...
DEBUG_DUMP_SCOPE ::= "$dumpscope"

// This is an internal-only feature, reserved for implementing the stdlib.
//"$$BEGIN_JAVA\n"  { if (supportPrivilegedInlineJava) {
SYNTHETIC_JAVA_TYPE::= "$java_type"

// Builtin Types.
INT_TYPE ::= "int"
LONG_TYPE ::= "long"
FLOAT_TYPE ::= "float"
DOUBLE_TYPE ::= "double"
BOOLEAN_TYPE ::= "boolean"
STRING_TYPE ::= "string"
CHAR_TYPE ::= "char"
TUPLE_TYPE ::= "tuple"
ONEOF ::= "oneof"
STRUCT_TYPE ::= "struct"
FUNCTION_TYPE ::= "function"
CONSUMER_FUNCTION_TYPE ::= "consumer"
PROVIDER_FUNCTION_TYPE ::= "provider"
LAMBDA ::= "lambda"
ALIAS ::= "alias"
ATOM ::= "atom"
NEWTYPE ::= "newtype"
UNWRAP ::= "unwrap"
INITIALIZERS ::= "initializers"
UNWRAPPERS ::= "unwrappers"

// Modifiers go here.
MUT ::= "mut"

// Module related bindings go here.
MODULE ::= "module"
BIND ::= "bind"
TO ::= "to"
AS ::= "as"
USING ::= "using"

CAST ::= "cast"

// Graph related things go here.
FUTURE ::= "future"
GRAPH ::= "graph"
ROOT ::= "root"
NODE ::= "node"
BLOCKING ::= "blocking"
MAYBE_BLOCKING ::= "blocking?"
LEFT_ARROW ::= "<-"
AT ::= "@"
BLOCKING_GET ::= "<-|"

// This up arrow is used for the pipe chain backreference term.
UP_ARROW ::= "^"

// Contract tokens.
CONTRACT ::= "contract"
IMPLEMENT ::= "implement"
REQUIRES ::= "requires"

UNDERSCORE ::= "_"

// Symbols related to builtin HTTP support go here.
// TODO(steving) The Http related types should all also require http:: namespacing.
// TODO(steving) This `http` module should be completely reimplemented as a proper claro_module_internal() target once possible.
HTTP_SERVICE ::= "HttpService"
HTTP_CLIENT ::= "HttpClient"
GET_HTTP_CLIENT::= "http::getHttpClient"
GET_BASIC_HTTP_SERVER_FOR_PORT ::= "http::getBasicHttpServerForPort"
// This is a major hack that simply allows the detection of the synthetic http optional stdlib module for which extra java deps will need to be added to the build.
//"http::startServerAndAwaitShutdown" ::= IDENTIFIER
HTTP_RESPONSE ::= "HttpResponse"
HTTP_SERVER ::= "HttpServer"
ENDPOINT_HANDLERS ::= "endpoint_handlers"

Script using Lua string pattern matching:

auto fname = "ClaroParser.cup";
auto txt = readfile(fname);
txt = txt.gsub("{:.-:}", "");
//txt = txt.gsub(".+parser code\n(.+)", "%1");
txt = txt.gsub("\nterminal%s+%S+%s+([^;]+);", "\n%%token %1").replace(",", "");
txt = txt.gsub("\nnonterminal [^\n]+", "");
txt = txt.gsub("\nprecedence ([^;]+);", "\n%%%1");
txt = txt.gsub(":[%w_]+", "");
txt = txt.gsub("\n[ \t]*\n+", "\n");
txt = txt.gsub("\n[ \t]*;\n", "\n\n");
//txt = txt.gsub("\n[ \t]*;\n", "%1\n");
//txt = txt.replace("::=", ":");
print(txt);

Support Type Aliasing

Sometimes type declarations can get super convoluted, I really want to encourage types to be declared explicitly in Claro, but I also think that I could make that an easier ask by allowing arbitrary Type Aliasing to let people shorten/simplify some type declarations.

As an example the type declaration on this lambda is complicated to read:

var partialLambda: function<|int, function<|int, int| -> int>| -> function<int -> int>> =
  lambda (first, f) -> second -> f(first, second);

A simple Type Alias would be a quick way to decompose the long type declaration into something more easily digestible in pieces:

alias IntFunc : function<int -> int>
alias IntBiFunc : function<|int, int| -> int>;
var partialLambda: function<|int, IntBiFunc| -> IntFunc> =
  lambda (first, l) -> second -> l(first, second);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.