|  | @@ -1529,9 +1529,12 @@ node_t *parse_block(list_t *tokens, size_t *pos) {
 | 
	
		
			
				|  |  |    EXPECT(LCB, "{");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    list_t *stmts = list_new();
 | 
	
		
			
				|  |  | -  while (!AT(EOF) && !AT(RCB))
 | 
	
		
			
				|  |  | +  while (!AT(EOF) && !AT(RCB)) {
 | 
	
		
			
				|  |  |      list_push(stmts, parse_stmt(tokens, pos));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    MATCH(SEMI);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    EXPECT(RCB, "}");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    return NODEL(PROGRAM, stmts);
 | 
	
	
		
			
				|  | @@ -2504,9 +2507,67 @@ const char *STD[][2] = {
 | 
	
		
			
				|  |  |      "func is_char(c): return type(c) == \"string\" && len(c) == 1\n"
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  {"math", NULL},
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    {NULL, NULL}
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +const struct {
 | 
	
		
			
				|  |  | +  const char *name;
 | 
	
		
			
				|  |  | +  const char *fname;
 | 
	
		
			
				|  |  | +  int arity;
 | 
	
		
			
				|  |  | +} MATHFUNCS[] = {
 | 
	
		
			
				|  |  | +  { "ceil", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "floor", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "trunc", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "round", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "abs", "fabs", 1 },
 | 
	
		
			
				|  |  | +  { "exp", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "sqrt", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "sinh", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "cosh", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "tanh", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "asinh", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "acosh", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "atanh", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "sin", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "cos", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "tan", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "asin", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "acos", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "atan", NULL, 1 },
 | 
	
		
			
				|  |  | +  { "atan2", NULL, 2 },
 | 
	
		
			
				|  |  | +  { "hypot", NULL, 2 },
 | 
	
		
			
				|  |  | +  { "random", "rand", 0 },
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  { NULL, 0 }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void genmathlib(void) {
 | 
	
		
			
				|  |  | +  buffer_t *buffer = buffer_new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  buffer_appends(buffer, "let Math = {\"tau\": 6.283185307179586, \"pi\": 3.141592653589793, \"e\": 2.718281828459045, \"inf\": INFINITY, \"nan\": NAN, ");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (size_t i = 0; MATHFUNCS[i].name; i++) {
 | 
	
		
			
				|  |  | +    if (MATHFUNCS[i].arity == 0)
 | 
	
		
			
				|  |  | +      buffer_fmt(buffer, "\"%s\": func () { inline `return qi_make_number(state, %s())` }", MATHFUNCS[i].name, MATHFUNCS[i].fname? MATHFUNCS[i].fname: MATHFUNCS[i].name);
 | 
	
		
			
				|  |  | +    else if (MATHFUNCS[i].arity == 1)
 | 
	
		
			
				|  |  | +      buffer_fmt(buffer, "\"%s\": func (x) { if type(x) != \"number\" { throw \"expected first argument to be: number, but got: \" + type(x) } inline `double n = %s(qi_get(state, \"x\")->value.number)`; inline `return qi_make_number(state, n)` }", MATHFUNCS[i].name, MATHFUNCS[i].fname? MATHFUNCS[i].fname: MATHFUNCS[i].name);
 | 
	
		
			
				|  |  | +    else
 | 
	
		
			
				|  |  | +      buffer_fmt(buffer, "\"%s\": func (x, y) { if type(x) != \"number\" { throw \"expected first argument to be: number, but got: \" + type(x) } if type(y) != \"number\" { throw \"expected second argument to be: number, but got: \" + type(y) } inline `double n = %s(qi_get(state, \"x\")->value.number, qi_get(state, \"y\")->value.number)`; inline `return qi_make_number(state, n)` }", MATHFUNCS[i].name, MATHFUNCS[i].fname? MATHFUNCS[i].fname: MATHFUNCS[i].name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (MATHFUNCS[i + 1].name)
 | 
	
		
			
				|  |  | +      buffer_append(buffer, ',');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  buffer_append(buffer, '}');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  size_t i;
 | 
	
		
			
				|  |  | +  for (i = 0; strcmp(STD[i][0], "math") != 0; i++) ;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  STD[i][1] = buffer_read(buffer);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  char *unescape(char *s) {
 | 
	
		
			
				|  |  |    buffer_t *buf = buffer_new();
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -3173,6 +3234,8 @@ int main(int argc, char **argv) {
 | 
	
		
			
				|  |  |    REQUIRED = list_new();
 | 
	
		
			
				|  |  |    CONSTANTS = table_new();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  genmathlib();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    char *out = compile_file("<stdin>", stdin);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    fwrite(out, sizeof(char), strlen(out), stdout);
 |