A Haskell inspired compiler that targets Lua.
Predefined:
- <string>
The EBNF grammar doesn't take into account operator precedence because the parser uses pratt parsing for operators, so its not inherently needed to be a part of the grammar.
<ident> ::= ([a-z] | [A-Z] | "_") ([a-z] | [A-Z] | [0-9] | "_")*
<integer> ::= [1-9] [0-9]*
<float> ::= ("0" "." [0-9]+) | ([1-9] [0-9]* "." [0-9]+)
<number> ::= <integer> | <float>
<bool> ::= "true" | "false"
<bin_op> ::= "==" | "!=" | ">" | ">=" | "<" | "<=" | "+" | "-" | "*" | "/" | "&&" | "||"
<bin_expr> ::= <primary> (<bin_op> <primary>)*
<primary> ::= "(" <expr> ")" | <number> | <ident> | <bool> | "-" <primary> | <set_access>
<pred_expr> ::= <bin_expr> | <bool> | <fn_call>
<fn_call> ::= ( <ident> | <set_access> ) ( <primary> | <bool> )+
<fn_param> ::= <ident>
| <set_selector>
| <set_destructure>
<case_expr> ::= <case> (<fn_param> ("," <fn_param>")* => <expr>)+
<set_destructure> ::= "{" <ident> ("," <ident>)* "}"
<set_selector> ::= "{" <ident> ":" <ident> "}
<set_access> ::= <ident> "." <ident> ("." <ident>)*
<expr> ::= <bin_expr>
| "if" <pred_expr> "then" <expr> ("elseif" <pred_expr> "then" <expr>)* "else" <expr>
| "let" <stmt>* "in" <expr>
| <fn_call>
| <fn_param> ("," <fn_param>")* "=>" ( <expr> | <case_expr> )
| "{" (<ident> "=" <expr>)+ ("," <ident> "=" <expr>)* "}"
<stmt> ::= <ident> ":=" <expr>
| <ident> ("," <ident>)* := <set_destructure>
| <set_destructure> = <expr> ("," <expr>)*
| <ident> "=" <expr>
| <set_access> "=" <expr>
<def> ::= "type" <ident> "::" <ident> ":" <ident> (<ident> ":" <ident>)*
| "class" <ident> <ident>+ "::" ( <ident> "::" ( <ident> ( "," <ident> )* "=>" <ident> )+ )+
| "class" <ident> <ident>+ "::" ( "(" <bin_op> ")" "::" ( <ident> "," <ident> "=>" <ident> )+ )+
| "instance" <ident> <ident> <ident>* "::" <ident> "=>" ( <expr> | <case_expr> )
| "module" "::" <ident>
<reserved_idents> ::= "class" | "type" | "instance" | "module" | "true"
| "false" | "if" | "then" | "else" | "elseif" | "let" | "in"
| "case"
A jace module always will follow the following structure
- Zero or more modules
- zero or more definitions
Additionally, the entry point module must contain a main procedure.
-- main procedure
def main :: ()
print "Hello World!"
Here is an abstracted overview of base language features.
-- the base "primitive" types are
Float, Integer, Bool, String
-- set or record types are be defined as
type Person ::
name : String
age : Int
-- union types may be defined as
type Bool :: True | False
-- union types may have polymoprhic parameters
type Either a b :: (Left a) | (Right b)
-- types may be aliased as so
type Vector3 :: [Float 3] -- array of 3 floats.
Variables, defined in Jace as a symbol containing a value, may not be top level. However, constants may be defined top level and are defined using '::' instead of ':='. Variables may be defined with in 'let-in' blocks and within procedures.
-- this is ok
const MATH_PI :: 3.14
def myComputation :: Integer, Integer => Integer
a, b =>
let
-- this is a local variable to the function's scope
my_result := a + b
in my_result
-- within procedure
def main :: ()
foo := 10
-- multiple assignment
a, b := 21, 22
print a b -- prints: 21 22
-- set destructuring
my_set := {foo = "Nice", bar = "...Nice"}
{first, second} := my_set
print first second -- prints: first second
Jace has support for algebraic types.
-- this is a basic sum type
type Bool :: True | False
-- a product type is defined as so
type Vector :: Vector3 Float Float Float
-- [1] | [2] | [3] |
-- 1) Type
-- 2) Variant Constructor
-- 3) Value constructor