The Foi language grammar in EBNF form, as verified here.
You can also use this grammar to validate code using the Online Foi-Toy web tool or the Foi-Toy CLI tool.
(*************** Program / Statements ***************)
Program := WhSp* ((StmtSemi | ExportStmtSemi) WhSp*)* (StmtSemiOpt | ExportStmtSemiOpt)? WhSp*;
Stmt := DefVarStmt | DefBlockStmt | DefTypeStmt | ExprAsOpt;
StmtSemi := Stmt? (WhSp* ";")+;
StmtSemiOpt := Stmt? (WhSp* ";")*;
ExportStmtSemi := ExportExpr (WhSp* ";")+;
ExportStmtSemiOpt := ExportExpr (WhSp* ";")*;
(*************** Whitespace ***************)
WhSp := Whitespace | Comment;
Whitespace := #"[\s]+" | (*u0085*) "" | (*u180e*) "" | (*u200b*) "" | (*u200c*) "" | (*u200d*) "" | (*u200e*) "" | (*u200f*) "";
Comment := LineComment | BlockComment;
LineComment := "//" #"[^\n/][^\n]*"? &("\n" | Epsilon);
BlockComment := "///" #"[^]*?///";
(*************** Core Syntax ***************)
Op := ComprOpNamed | NamedBoolOp | SymbolicBoolOp | SymbolicOp;
NamedBoolOp := #"[?!](?:empty|has|and|in|as|or)";
SymbolicBoolOp := "?<=>" | "!<=>" | #"[?!](?:>=|<=|<>|\$=)" | #"[?!][=<>]";
SymbolicOp := "~<<" | "~<*" | "..." | ".." | "+>" | "<+" | "#>" | "~<" | "$+" | #"[+\-*/?!.']";
ReservedWord := Empty | Boolean | NamedKeywordNoEmpty | NativeTypeNoEmpty | BuiltIn;
Empty := "empty";
Boolean := "true" | "false";
Keyword := NamedKeywordNoEmpty | ":as" | ":over";
NamedKeywordNoEmpty := "def" | "defn" | "deft" | "import" | "export";
NativeType := Empty | NativeTypeNoEmpty;
NativeTypeNoEmpty := "int" | "integer" | "float" | "bool" | "boolean" | "str" | "string";
BuiltIn := "Id" | "None" | "Maybe" | "Left" | "Right" | "Either" | "Promise" | "PromiseSubject" | "PushStream" | "PushSubject" | "PullStream" | "PullSubject" | "Channel" | "Gen" | "IO" | "Value" | "Number" | "List";
(*************** Number Literals ***************)
NumberLit := Base10Number | EscBase10 | BinaryInteger | HexInteger | OctalInteger | UnicodeChar | MonadicNumber;
Esc := "\\";
BinaryEsc := Esc "b";
HexEsc := Esc "h";
OctalEsc := Esc "o";
UnicodeEsc := Esc "u";
MonadicEsc := Esc "@";
Base10Number := "-"? Base10Digit+ ("." Base10Digit+)?;
Base10Digit := OctalDigit | "8" | "9";
EscBase10 := Esc EscNum;
EscNum := "-"? EscNumDigits ("." EscNumDigits)?;
EscNumDigits := Base10Digit+ ("_" EscNumDigits)?;
BinaryInteger := BinaryEsc "-"? BinaryDigit+;
BinaryDigit := "0" | "1";
HexInteger := HexEsc HexNum;
HexNum := "-"? HexDigit+;
HexDigit := Base10Digit | #"[a-fA-F]";
OctalInteger := OctalEsc "-"? OctalDigit+;
OctalDigit := BinaryDigit | #"[2-7]";
UnicodeChar := UnicodeEsc HexDigit+;
MonadicNumber := MonadicEsc (EscNum | HexNum);
PositiveIntLit := Base10Digit+ | (Esc EscNumDigits) | (HexEsc HexDigit+) | (OctalEsc OctalDigit+) | (BinaryEsc BinaryDigit+);
(*************** String Literals ***************)
StrLit := PlainStr | SpacingStr | InterpStr | InterpSpacingStr;
InterpEsc := "`";
InterpSpacingEsc := Esc InterpEsc;
PlainStr := '"' (#'[^"]' | '""')* '"';
SpacingStr := Esc PlainStr;
InterpStr := InterpEsc InterpLit;
InterpSpacingStr := InterpSpacingEsc InterpLit;
InterpLit := '"' (#'[^"`]' | '""' | "`" WhSp* InterpExprAsOpt? WhSp* "`")* '"';
InterpExprAsOpt := !('`"') ExprAsOpt;
(* NOTE: the above `InterpExprAsOpt` production has a negative-lookahead *)
(* to avoid a grammar ambiguity with nested interpolated-strings. *)
(*************** Data Structures ***************)
DataStructLit := RecordTupleLit | SetLit;
RecordTupleLit := "<" WhSp* RecordTupleEntryList WhSp* ">";
RecordTupleEntryList := ("," WhSp*)* (RecordTupleEntry (WhSp* "," WhSp* RecordTupleEntry?)*)?;
RecordTupleEntry := RecordTupleValue | PickValue | RecordProperty;
RecordTupleValue := Empty | Boolean | NumberLit | StrLit | DataStructLit | IdentifierExpr | CallExpr | ("(" WhSp* RecordTupleValue WhSp* ")");
PickValue := "&" IdentifierExpr;
RecordProperty := (":" PropertyExpr) | ((("%" ("#" | IdentifierExpr | StrLit)) | PropertyExpr) WhSp* ":" WhSp* RecordTupleValue);
PropertyExpr := Identifier | PositiveIntLit;
SetLit := "<[" WhSp* SetEntryList WhSp* "]>";
SetEntryList := ("," WhSp*)* (SetEntry (WhSp* "," WhSp* SetEntry?)*)?;
SetEntry := RecordTupleValue | PickValue;
(*************** Misc Expressions ***************)
Expr := ExprNoBlock | BlockExpr | ComprExpr | DoComprExpr | DoLoopComprExpr | GroupedExpr;
GroupedExpr := "(" WhSp* Expr WhSp* ")";
ExprNoBlock := OperandExpr | DefFuncExpr | AssignmentExpr | GuardedExpr | MatchExpr | ExprAccessExpr | GroupedExprNoBlock;
GroupedExprNoBlock := "(" WhSp* ExprNoBlock WhSp* ")";
OperandExpr := BareOperandExpr | UnaryExpr | BinaryExpr | GroupedOperandExpr;
GroupedOperandExpr := "(" WhSp* OperandExpr WhSp* ")";
BareOperandExpr := Empty | BareOperandExprNoEmpty | GroupedBareOperandExpr;
GroupedBareOperandExpr := "(" WhSp* BareOperandExpr WhSp* ")";
BareOperandExprNoEmpty := Boolean | NumberLit | StrLit | DataStructLit | ClosedRangeExpr | IdentifierExpr | OpFuncExpr | CallExpr | GroupedBareOpExprNoEmp;
GroupedBareOpExprNoEmp := "(" WhSp* BareOperandExprNoEmpty WhSp* ")";
ExprAsOpt := Expr | ExprNoBlockAsOpt | ((BlockExpr | GroupedExpr | ("(" WhSp* (ComprExpr | DoComprExpr | DoLoopComprExpr) WhSp* ")")) WhSp* AsAnnotationExpr) | GroupedExprAsOpt;
GroupedExprAsOpt := "(" WhSp* ExprAsOpt WhSp* ")";
ExprNoBlockAsOpt := ExprNoBlock | OperandExprAsOpt | ("(" WhSp* (GuardedExpr | MatchExpr | ExprAccessExpr) WhSp* ")" WhSp* AsAnnotationExpr) | GroupedExprNoBlockAsOpt;
GroupedExprNoBlockAsOpt := "(" WhSp* ExprNoBlockAsOpt WhSp* ")";
OperandExprAsOpt := OperandExpr | BareOperandExprAsOpt | ((UnaryExpr | BinaryExpr) WhSp+ AsAnnotationExpr) | ("(" WhSp* (UnaryExpr | BinaryExpr) WhSp* ")" WhSp* AsAnnotationExpr) | GroupedOperandExprAsOpt;
GroupedOperandExprAsOpt := "(" WhSp* OperandExprAsOpt WhSp* ")";
BareOperandExprAsOpt := BareOperandExpr | BareOpExprNoEmptyAsOpt | (BareOperandExpr WhSp+ AsAnnotationExpr) | ("(" WhSp* BareOperandExpr WhSp* ")" WhSp* AsAnnotationExpr) | GroupedBareOprExprAsOpt;
GroupedBareOprExprAsOpt := "(" WhSp* BareOperandExprAsOpt WhSp* ")";
BareOpExprNoEmptyAsOpt := BareOperandExprNoEmpty | (BareOperandExprNoEmpty WhSp+ AsAnnotationExpr) | ("(" WhSp* BareOperandExprNoEmpty WhSp* ")" WhSp* AsAnnotationExpr) | GrpBareOpExprNoEmpAsOpt;
GrpBareOpExprNoEmpAsOpt := "(" WhSp* BareOpExprNoEmptyAsOpt WhSp* ")";
ExprNoBlockGroupedAsOpt := ExprNoBlock | GroupedExprNoBlockAsOpt;
AsAnnotationExpr := ":as" WhSp+ NamedType;
OpFuncExpr := "(" (Op | DotAngleExpr | DotBracketExpr | "[]") "'"? ")";
ExprAccessExpr := (ExprNoBlock | GroupedExprAsOpt) (SingleAccessExpr | MultiAccessExpr);
AssignmentExpr := (Identifier | IdentifierSingleExpr) WhSp* ":=" WhSp* ExprAsOpt;
UnaryExpr := SymbolicUnaryExpr | NamedUnaryExpr | GroupedUnaryExpr;
GroupedUnaryExpr := "(" WhSp* UnaryExpr WhSp* ")";
SymbolicUnaryExpr := (("?" | "!") (BareOperandExprNoEmpty | BareOpExprNoEmptyAsOpt | GroupedExprAsOpt)) | (("?" | "!") WhSp+ (OperandExpr | GroupedExprAsOpt)) | ((BareOperandExpr | GroupedExprAsOpt) "'");
NamedUnaryExpr := (("?empty" | "!empty") GroupedExprAsOpt) | (("?empty" | "!empty") WhSp+ (OperandExpr | GroupedExprAsOpt));
BinaryExpr := SymbolicBinaryExpr | NamedBoolBinaryExpr | GroupedBinaryExpr;
GroupedBinaryExpr := "(" WhSp* BinaryExpr WhSp* ")";
SymbolicBinaryExpr := (OperandExpr | OperandExprAsOpt | GroupedExprAsOpt) WhSp* SymbolicRightExpr;
SymbolicRightExpr := (("$+" | "<+" | "+>" | #"[+\-*/]") WhSp* (OperandExpr | OperandExprAsOpt | GroupedExprAsOpt)) | SymbolicBoolRightExpr | PipelineRightExpr;
SymbolicBoolRightExpr := SymbolicBoolOp WhSp* (OperandExpr | GroupedExprAsOpt);
NamedBoolBinaryExpr := (GroupedExprAsOpt NamedBoolRightExpr) | ((OperandExpr | GroupedExprAsOpt) WhSp+ NamedBoolRightExpr);
NamedBoolRightExpr := (NamedBoolOp GroupedExprAsOpt) | (NamedBoolOp WhSp+ (OperandExpr | GroupedExprAsOpt)) | (("?" | "!") "as" WhSp* NativeType);
PipelineRightExpr := "#>" WhSp* (OperandExpr | BlockExpr | GroupedExprAsOpt);
ImportExpr := "import" WhSp+ PlainStr;
ExportExpr := "export" WhSp+ "{" WhSp* ExportBindingsList WhSp* "}";
ExportBindingsList := ExportBinding (WhSp* "," WhSp* ExportBinding)* (WhSp* ",")?;
ExportBinding := ExportNamedBinding | ExportConciseBinding;
ExportNamedBinding := Identifier WhSp* ":" WhSp* Identifier MultiAccessExpr?;
ExportConciseBinding := ":" Identifier SingleAccessExpr?;
(*************** Identifier / Access / Range Expressions ***************)
Identifier := (#"(?!(?:[0-9]+|~each|~map|~filter|~fold|~foldR|~cata|~chain|~bind|~flatMap|~ap|~foldMap)\b)[a-zA-Z0-9_~]+(?<!\b(?:def|defn|deft|import|export|empty|true|false|int|integer|float|bool|boolean|str|string|Id|None|Maybe|Left|Right|Either|Promise|PromiseSubject|PushStream|PushSubject|PullStream|PullSubject|Channel|Gen|IO|Value|Number|List))") | #"[0-9]+~" | (ComprOpNamed #"[a-zA-Z0-9_~]"+) | (#"[a-zA-Z0-9_~]"+ ReservedWord);
IdentifierExpr := "#" | "@" | Identifier | BuiltIn | IdentifierSingleExpr | IdentifierMultiExpr | AtExpr;
IdentifierSingleExpr := ("#" | Identifier | BuiltIn) SingleAccessExpr;
IdentifierMultiExpr := ("#" | Identifier | BuiltIn) MultiAccessExpr;
AtExpr := (Identifier | BuiltIn | IdentifierSingleExpr) "@";
SingleAccessExpr := (WhSp* (DotSingleIdentifier | BracketExpr))+;
MultiAccessExpr := (WhSp* (DotMultiIdentifier | BracketExpr | DotBracketExpr | DotAngleExpr))+;
DotSingleIdentifier := "." WhSp* (("-"? Base10Digit+) | Identifier | BuiltIn | IdentifierSingleExpr);
DotMultiIdentifier := "." WhSp* (Base10Digit+ | Identifier | BuiltIn | IdentifierMultiExpr);
BracketExpr := "[" WhSp* ExprNoBlockAsOpt WhSp* "]";
DotBracketExpr := ".[" WhSp* RangeExpr WhSp* "]";
RangeExpr := (ClosedRangeExpr | LeadingRangeExpr | TrailingRangeExpr);
ClosedRangeExpr := (ExprNoBlock | GroupedExprAsOpt) ".." (ExprNoBlock | GroupedExprAsOpt);
LeadingRangeExpr := (ExprNoBlock | GroupedExprAsOpt) "..";
TrailingRangeExpr := ".." (ExprNoBlock | GroupedExprAsOpt);
DotAngleExpr := ".<" WhSp* AnglePropertyList WhSp* ">";
AnglePropertyList := PropertyExpr (WhSp* "," WhSp* PropertyExpr)* (WhSp* ",")?;
(*************** Variable Definitions / Destructuring / Blocks ***************)
DefVarStmt := "def" WhSp+ (Identifier | DestructureTarget) WhSp* ":" WhSp* (ExprAsOpt | ImportExpr);
DestructureTarget := "<" WhSp* DestructureDefList WhSp* ">";
DestructureDefList := DestructureDef (WhSp* "," WhSp* DestructureDef)* (WhSp* ",")?;
DestructureDef := DestructureNamedDef | DestructureConciseDef | DestructureCapture;
DestructureNamedDef := Identifier WhSp* ":" WhSp* (Identifier | BracketExpr) MultiAccessExpr?;
DestructureConciseDef := ":" Identifier SingleAccessExpr?;
DestructureCapture := "#" Identifier;
DefBlockStmt := "def" WhSp* BlockExprVarDef;
BlockExprVarDef := BlockDefsInit WhSp* BareBlockExpr;
BlockExpr := BlockDefsInitOpt? WhSp* BareBlockExpr;
BareBlockExpr := "{" WhSp* (StmtSemi WhSp*)* StmtSemiOpt? WhSp* "}";
BlockDefsInit := "(" WhSp* VarDefInitList WhSp* ")";
VarDefInitList := VarDefInit (WhSp* "," WhSp* VarDefInit)* (WhSp* ",")?;
VarDefInit := Identifier WhSp* ":" WhSp* ExprNoBlockAsOpt;
BlockDefsInitOpt := "(" WhSp* VarDefInitOptList WhSp* ")";
VarDefInitOptList := ("," WhSp*)* (VarDefInitOpt (WhSp* "," WhSp* VarDefInitOpt?)*)?;
VarDefInitOpt := (Identifier (WhSp* ":" WhSp* ExprNoBlockAsOpt)?) | DestructureTarget;
(*************** Decision Making: Guard, Pattern Matching ***************)
CondClause := ("?" | "!") BracketExpr;
GuardedExpr := CondClause ":" WhSp* ExprAsOpt;
MatchExpr := IndepMatchExpr | DepMatchExpr;
IndepMatchExpr := "?{" WhSp* IndepPatternStmts WhSp* "}";
IndepPatternStmts := IndepPatternStmtNoSemi | ((IndepPatternStmt WhSp*)+ (ElseStmt | IndepPatternStmtNoSemi)?) | ElseStmt;
IndepPatternStmtNoSemi := IndepCondClause MatchConsequentNoSemi;
IndepPatternStmt := IndepCondClause MatchConsequent (WhSp* ";")*;
IndepCondClause := ("?" | "!" | Epsilon) BracketExpr;
MatchConsequentNoSemi := ":" WhSp* ExprAsOpt | BlockExpr;
MatchConsequent := ":" WhSp* ((ExprAsOpt WhSp* ";") | BlockExpr);
ElseStmt := "?"? MatchConsequentNoSemi (WhSp* ";")*;
DepMatchExpr := "?(" WhSp* ExprNoBlockAsOpt WhSp* "){" WhSp* DepPatternStmts WhSp* "}";
DepPatternStmts := DepPatternStmtNoSemi | ((DepPatternStmt WhSp*)+ (ElseStmt | DepPatternStmtNoSemi)?) | ElseStmt;
DepPatternStmtNoSemi := DepCondClause MatchConsequentNoSemi;
DepPatternStmt := DepCondClause MatchConsequent (WhSp* ";")*;
DepCondClause := ("?" | "!" | Epsilon) "[" WhSp* DepCondExprList WhSp* "]";
DepCondExprList := (ExprNoBlockGroupedAsOpt | DepCondBinaryBoolExpr) (WhSp* "," WhSp* (ExprNoBlockGroupedAsOpt | DepCondBinaryBoolExpr))* (WhSp* ",")?;
DepCondBinaryBoolExpr := NamedBoolRightExpr | SymbolicBoolRightExpr | ("(" WhSp* DepCondBinaryBoolExpr WhSp* ")");
(*************** Loops/Comprehensions ***************)
ComprExpr := (((ComprRangeNoEachExpr WhSp+ ComprOpNoEach) | (ComprRangeEachExpr WhSp+ ComprOpEach) | (ComprExpr WhSp+ ComprOp)) WhSp+ ComprIterationExpr) | ("(" WhSp* ComprExpr WhSp* ")");
ComprRangeNoEachExpr := IdentifierExpr | CallExpr | DataStructLit | ClosedRangeExpr | ExprAccessExpr | DoComprExpr | DoLoopComprExpr | ("(" WhSp* (ComprRangeNoEachExpr | MatchExpr) WhSp* ")");
ComprRangeEachExpr := CondClause | ComprRangeNoEachExpr | ("(" WhSp* ComprRangeEachExpr WhSp* ")");
ComprOp := ComprOpNoEach | ComprOpEach;
ComprOpNamed := ComprOpEach | ComprOpNamedNoEach;
ComprOpEach := "~each";
ComprOpNoEach := ComprOpNamedNoEach | "~<";
ComprOpNamedNoEach := "~map" | "~filter" | "~fold" | "~foldR" | "~cata" | "~chain" | "~bind" | "~flatMap" | "~ap" | "~foldMap";
ComprIterationExpr := BlockExpr | ComprIterNoBlockExpr | GroupedExprAsOpt;
ComprIterNoBlockExpr := ComprExpr | IdentifierExpr | CallExpr | ExprAccessExpr | ("(" WhSp* ComprIterNoBlockExpr WhSp* ")");
DoComprExpr := (Identifier | BuiltIn) WhSp+ "~<<" WhSp* DoBlockExpr;
DoBlockExpr := DoBlockDefsInitOpt? WhSp* DoBareBlockExpr;
DoBareBlockExpr := "{" WhSp* (DoStmtSemi WhSp*)* (DoStmtSemiOpt | DoFinalUnwrapExpr)? WhSp* "}";
DoBlockDefsInitOpt := "(" WhSp* DoVarDefInitOptList WhSp* ")";
DoVarDefInitOptList := ("," WhSp*)* (DoVarDefInitOpt (WhSp* "," WhSp* DoVarDefInitOpt?)*)?;
DoVarDefInitOpt := (Identifier (WhSp* ("::" | ":") WhSp* ExprNoBlockAsOpt)?) | DestructureTarget;
DoDefVarStmt := "def" WhSp+ (Identifier | DestructureTarget) WhSp* "::" WhSp* ExprAsOpt;
DoStmtSemi := DoStmt? (WhSp* ";")+;
DoStmt := Stmt | DoDefVarStmt;
DoStmtSemiOpt := DoStmt? (WhSp* ";")*;
DoFinalUnwrapExpr := "::" ExprNoBlockAsOpt (WhSp* ";")*;
DoLoopComprExpr := ((DoLoopComprRangeExpr WhSp+) | ("(" WhSp* DoLoopComprRangeExpr WhSp* ")")) "~<*" WhSp DoLoopIterationExpr;
DoLoopComprRangeExpr := ComprRangeNoEachExpr | ComprExpr;
DoLoopIterationExpr := DoBlockExpr | DoLoopIterNoBlockExpr;
DoLoopIterNoBlockExpr := IdentifierExpr | CallExpr | ExprAccessExpr | ("(" WhSp* DoLoopIterNoBlockExpr WhSp* ")");
(*************** Functions ***************)
DefFuncExpr := "defn" (WhSp+ Identifier "@"?)? WhSp* ("(" WhSp* (ParameterList | GatherParameter)? WhSp* ")")+ FuncMeta? WhSp* FuncBody;
ParameterList := VarDefInitOpt (WhSp* "," WhSp* VarDefInitOpt)*;
GatherParameter := "*" Identifier;
FuncMeta := (WhSp* FuncPrecondList (WhSp+ FuncOverClause)? (WhSp+ FuncAsClause)?) | (WhSp* FuncOverClause WhSp+ FuncAsClause) | (WhSp* FuncPrecondList) | (WhSp+ FuncOverClause) | (WhSp+ FuncAsClause);
FuncPrecondList := FuncPrecond (WhSp+ FuncPrecond)*;
FuncPrecond := CondClause ":" WhSp* ExprNoBlockGroupedAsOpt;
FuncOverClause := ":over" WhSp* "(" WhSp* Identifier (WhSp* "," WhSp* Identifier)* WhSp* ")";
FuncAsClause := ":as" WhSp+ Identifier;
FuncBody := ("^" WhSp* (ExprNoBlock | GroupedExprAsOpt)) | PipelineRightExpr | FuncBodyBlock;
FuncBodyBlock := "{" WhSp* (FuncBodyStmtSemi WhSp*)* FuncBodyStmtSemiOpt? WhSp* "}";
FuncBodyStmtSemi := FuncBodyStmt (WhSp* ";")+;
FuncBodyStmtSemiOpt := FuncBodyStmt (WhSp* ";")*;
FuncBodyStmt := Stmt | ("^" WhSp* ExprAsOpt);
(*************** Function Calls ***************)
CallExpr := PrefixCallExpr | PartialCallExpr | AtCallExpr;
PrefixCallExpr := ExprNoBlockGroupedAsOpt WhSp* "(" CallArgs ")";
PartialCallExpr := ExprNoBlockGroupedAsOpt WhSp* "|" CallArgs "|";
CallArgs := (WhSp* CallArgList? WhSp*) | (Op "'"?);
CallArgList := ("," WhSp*)* (CallArgExpr (WhSp* "," WhSp* CallArgExpr?)*)?;
CallArgExpr := ExprAsOpt | NamedArgExpr | (("..." WhSp*)? CallArgExpr);
NamedArgExpr := ((":" Identifier) | (Identifier WhSp* ":" WhSp* ExprAsOpt)) | ("(" WhSp* NamedArgExpr WhSp* ")");
AtCallExpr := "None@" | (("@" | AtExpr | ((Identifier | BuiltIn | IdentifierSingleExpr) WhSp+ "@")) WhSp* ExprNoBlockGroupedAsOpt);
(*************** Types ***************)
DefTypeStmt := "deft" WhSp+ Identifier ((WhSp+ NoFuncType) | (WhSp* FuncType)) WhSp*;
NoFuncType := UnionType | NoUnionType;
UnionType := NoUnionType (WhSp* "|" WhSp* NoUnionType)+;
NoUnionType := SimpleType | DataStructType | NestedType | GroupedType;
SimpleType := NamedType | PlainStr | SpacingStr | NumberLit | Boolean;
NamedType := ((Identifier | BuiltIn) ("." (Identifier | BuiltIn))*) | NativeType;
NestedType := NamedType WhSp* GroupedType;
GroupedType := "{" WhSp* (NoUnionType | (UnionType (WhSp* "|")?) | FuncType) WhSp* "}";
DataStructType := "<" WhSp* DataStructTypeList? WhSp* ("," WhSp*)? ">";
DataStructTypeList := DataStructFinalValType | ((DataStructValueType | DataStructFieldType) (WhSp* "," WhSp* (DataStructValueType | DataStructFieldType))* (WhSp* "," WhSp* DataStructFinalValType)?);
DataStructValueType := NoFuncType | GroupedType;
DataStructFinalValType := "*" NoUnionType;
DataStructFieldType := Identifier WhSp* ":" WhSp* DataStructValueType;
FuncType := "(" WhSp* FuncTypeArgList? WhSp* ("," WhSp*)? ")" WhSp* "^" WhSp* "?"? NoUnionType;
FuncTypeArgList := FuncTypeFinalArg | (FuncTypeArg (WhSp* "," WhSp* FuncTypeArg)* (WhSp* "," WhSp* FuncTypeFinalArg)?);
FuncTypeArg := "?"? NoUnionType;
FuncTypeFinalArg := FuncTypeArg | ("*" NoUnionType);
Here are some examples of Foi code to test various aspects of this grammar:
(recognized whitespace characters)
\u0009\u000a\u000b\u000c\u000d\u0020\u0085\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u200e\u200f\u2028\u2029\u202f\u205f\u3000\ufeff
def a: 1;
def b:1; // hello
def c : 1 ; ;;
def d: /// hello
/// 3;
def /// e: 3;/// f: 4;
def 123a: empty;
def a123: empty;
def 123~: empty;
def ~123: empty;
def Value~: empty;
def ~Value: empty;
def empty~: empty;
def ~empty: empty;
def int~: empty;
def ~int: empty;
def ~eachA: empty;
def ~each~: empty;
def a~each: empty;
defn 123a() ^empty;
defn a123() ^empty;
defn 123~() ^empty;
defn ~123() ^empty;
defn Value~() ^empty;
defn ~Value() ^empty;
defn empty~() ^empty;
defn ~empty() ^empty;
defn int~() ^empty;
defn ~int() ^empty;
defn ~eachA() ^empty;
defn ~each~() ^empty;
defn a~each() ^empty;
123;
-123;
123.456;
-123.456;
\123_456;
\-123_456;
\123_456.78_9;
\-123_456.78_9;
\b10110;
\b-10110;
\hf123;
\h-f123;
\o123;
\o-123;
\uf123;
\@123_456.78_9;
\@-f123;
"Hello world";
"Hello, ""Santa""!";
"Here's a
multiline string";
\"A single line
string with whitespace collapsing, defined across multiple
lines";
\`"A single line (with
whitespace collapsing), and a single `` backtick";
`"Special number: `-3.1415962`
Name: `name`
Greeting: `\`"Hello world"`
Reaction: `\"Yay!"`
Reply: `"Ok."`
!";
<>;
< >;
<true>;
<1,2,3>;
<a:1>;
< a: 1, b: "ok" >;
<
,,&v.x.[3..].<a,b> , "Hello" , 3,,4, :foo,
yes: empty, fn(1),
%x.y.z: false,
%"Hello World": 2,
%\`" this
is `adverb` ""crazy""!": 42,
%bar:<1>,,
>;
<[]>;
<[ 1, 2, 2 ]>;
2+3+4;
2 + 3 + 4;
(2 + 3) + 4;
2 + (3 + 4);
x + y + true;
?x;
? x;
?(x);
!x;
! x;
!(x);
x ?and y !and z;
(x ?and !y) !or (z / 2);
(x ?and !y)!or(z/2);
?empty y;
?x;
x ?in y;
x?>y;
x ?> y;
x ?= y;
x != y;
x ?$= y;
x !$= y;
def cb: defn(x)^x;
def (x: 2, y: empty) { f(x.2,x.-1); };
def < x : y.2, #obj, :w.4, z: [i+1], >: obj;
def f: defn(*x)^x.[1..];
defn add(x)(y,<:z>)
?[x.y]: y(z[2])
![x]: z(3)
:over(z, w)
:as Whatever
{
z := 2;
?{?: 42;};
?{
?[z]: fn(g);
[w]: w;
![x]: { fn(g) };
?[y]: (v, <:z>) { fn(g); }
?: 42
};
?( fn(g) ){
[?> y]: g;
?[ x, z . y [3] ]: g
};
?{ ?[x]: x };
?(x){ ?[x]: x };
?{ ?[x]: x; ?[y]: y };
?(x){ ?[x]: x; ?[y]: y };
?[z]: (g: z) { fn(g) };
x.[y..z];
y.<first,last>;
(+)(1,2,3,...nums);
(+')'(1,,3);
(')(+)(1,,3);
myFn|2,,3|;
myFn(3,x:2);
myFn'|3,x:2|;
^42
};
1..3 ~each log;
?[x] ~each (x,y:2) { x };
foo ~map ![x] ~each foo;
x . y [3].[1..3] .<a,b,> ~filter { y };
x #> (y(#.y,2) #> z);
def cb1: f(2) +> g +> h(3)(4);
def cb2: f(2) <+ g <+ h(3)(4);
def cb3: f +> (defn(v) ^v) +> g;
defn myFn(x) #> f(#..3);
2..4 #> { f(#.0) };
List ~<< {
def x:: getSomething();
def y: uppercase(x);
def z:: another(y);
z.0
};
IO ~<< (x:: getSomething()) {
def y: uppercase(x);
def z:: another(y);
::prepareValue(z);
};
Promise ~<* {
def respE:: getSomething();
Either ~<< (resp:: respE) {
printResp(resp);
};
};
urls ~map fetch ~<* (resp) {
def v:: processResp(resp);
def success:: storeVal(v);
?[success]: log(v);
};
f(@);
f(@2);
f(@ 2);
f(@(2));
f(@ (2));
f(Id@);
f(Id@2);
f(Id @2);
f(Id@ 2);
f(Id @ 2);
f(Id@(2));
f(Id@ (2));
f(Id @ (2));
f(Either.Right@);
f(Either.Right@2);
f(Either.Right @2);
f(Either.Right@ 2);
f(Either.Right @ 2);
f(Either.Right@(2));
f(Either.Right@ (2));
f(Either.Right @ (2));
(*)(getQty(order,item), getPrice(item)) :as float;
?3 :as bool;
?(3) :as bool;
?(3):as bool;
3 * 2 :as int;
3 * (2 :as int);
(3 * 2) :as int;
(3 * 2):as int;
3 ?as bool;
(?(x){
?[?as int]: f(#) :as bool;
?: # :as bool
}):as bool;
deft F (?X) ^G;
deft X(Y,Z) ^empty;
deft Y(_) ^Either;
deft Z() ^Either;
deft W <
a: Q,
b: S | int,
c: U | {(*string) ^{bool|42}},
d: < int, string, *bool, >,
*< int, int >,
>;
deft Q(R) ^PushStream;
deft R(_) ^PushStream;
deft S(T) ^ PushStream;
deft T(*_)^ _;
deft U(int, string, *float) ^bool;
deft V Left | Right;
deft A { Left | Right };
deft B(str, *{(int)^int}) ^{"yes"|"no"};
def x: import "X";
def < :log >: import "#Std";
export { :x, :y, z: zzz, };
All code and documentation are (c) 2022-2023 Kyle Simpson and released under the MIT License. A copy of the MIT License is also included.