use Grammar::Tracer;
grammar toy {
rule TOP { <.ws> <statement>* %% ';' }
proto rule statement {*}
rule statement:import { 'import' <value:string> }
rule statement:print { 'print(' ~ ')' <value> }
rule statement:vdecl { 'var' <ident> [ '=' <value> | 'in' <ident> ] }
rule statement:ifelse { 'if' <condition> '{' ~ '}' <TOP> [ 'else' '{' ~ '}' <TOP> ]? }
rule statement:while { 'while' <condition> '{' ~ '}' <TOP> }
rule statement:expr { <expr> }
proto rule value {*}
token value:boolean { 'true' | 'false' }
token value:string { '"' ~ '"' <-["]>* }
token value:number { \d+ [ '.' \d+ ]? }
rule value:array { '[' ~ ']' <value>* %% ',' }
rule value:object { '{' ~ '}' [ [ <ident> ':' <value> ]* %% ',' ] }
token value:atkey { <value> '[' ~ ']' <value:string> }
token value:dotkey { <value> '.' <ident> }
token value:ident { <ident> }
token condition { '(' ~ ')' [ '!'? <expr> ] }
rule expr(:$left) # pass :left to avoid grammar left recursion
{ <prefix-op> <expr>
| '(' ~ ')' <expr>
| <value>
| <statement:vdecl>
| { say 42; die } <{'<postfixed-expr>' unless $left}>
| { say 99; die } <{'<infixed-exprs>' unless $left}> }
rule postfixed-expr { <expr(:left)> <postfix-op> }
rule infixed-exprs { <expr(:left)> <infix-op> <expr> }
token prefix-op { '+' | '-' | '++' }
token postfix-op { '+' | '-' | '++' }
token infix-op { '+' | '-' | '*' | '/' | '<' | '>' | '==' }
regex ws { <!ww> \s* [ [ '//' \N* || '/*' ~ '*/' [ <!before '*/'> . ]* ] \s* ]* }
}
say toy.parse: slurp 'test.toy';
// oneline comment
/* multiline comment */
import "standard"; //imports built-in libraries, or .toy files
import "external.toy";
print("hello world\n"); //print is a keyword, rather than a function - useful for debugging
//five types of data - booleans, strings, numbers, arrays and dictionaries
var a = true;
var b = false;
var c = "Hello world"; //only allow double-quotes for strings
var d = 42; //numbers can optionally have trailing decimal places...
var e = [a, b, c, d, "hello world", 42]; //mixed types allowed in arrays and objects
var f = { //objects are key-value pairs, essentially JSON
hello: "world" //"hello" is the string key - this is for familiarity with JS
};
f["hello"] == f.hello //the '.' operator only works for string keys
/*
if (!b) {
//conditional
} else {
//otherwise
}
var sentinel = 0;
while (sentinel < 10) {
sentinel++; //can also use '+=' operator
}
for (var element in e) { //array iteration
e[0] == element
}
for (var element in f) { //dictionary iteration
element.key
element.value
}
//functions can be defined in several ways
var g = function(arguments) {
//
}
var h = (arguments) => {
//
}
var i = x => x; //parenthesis and braces are optional, when there is only one argument or one expression respectfully
//closures are explicitly supported
var createCounter = function() {
var count = 0;
return () => ++count;
}
//an incorrect number of arguments is invalid, but the 'rest' parameter is allowed, creating an array
var j = function(...args) {
//
}*/