Switch to curly braces for scoping instead of indentation
This commit is contained in:
parent
9337490f5b
commit
893aa5d979
21 changed files with 692 additions and 728 deletions
|
|
@ -187,7 +187,7 @@ int main(int argc, char ** argv)
|
|||
if (dump_ast)
|
||||
{
|
||||
std::cout << "Input file " << filenames.back() << " AST dump:\n\n";
|
||||
if (auto function_definition = std::get_if<ast::function_definition>(parsed.back().get()))
|
||||
if (auto function_definition = std::get_if<ast::function_definition>(root.get()))
|
||||
ast::print(std::cout, *function_definition->statements);
|
||||
std::cout << "\n" << std::flush;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,81 +4,92 @@ import components
|
|||
import ecs
|
||||
|
||||
const x = 10s // deduced type i16
|
||||
var y = 14u // deduced type u32
|
||||
var z: f64 = 3.14l
|
||||
mut y = 14u // deduced type u32
|
||||
mut z: f64 = 3.14l
|
||||
|
||||
func fma(x: f32, y: f32, z: f32) -> f32:
|
||||
return x * y + z
|
||||
func fma(x: f32, y: f32, z: f32) -> f32 {
|
||||
return x * y + z
|
||||
}
|
||||
|
||||
struct vec2:
|
||||
x: f32
|
||||
y: f32
|
||||
struct vec2 {
|
||||
x: f32
|
||||
y: f32
|
||||
}
|
||||
|
||||
// pass by value
|
||||
func length(v: vec2) -> f32:
|
||||
return math.sqrt(v.x * v.x + v.y * v.y)
|
||||
func length(v: vec2) -> f32 {
|
||||
return math.sqrt(v.x * v.x + v.y * v.y)
|
||||
}
|
||||
|
||||
// return type deduced as u64
|
||||
func merge(x: u32, y: u32):
|
||||
return (x as u64) or ((y as u64) << 32)
|
||||
func merge(x: u32, y: u32) -> u64 {
|
||||
return (x as u64) or ((y as u64) << 32)
|
||||
}
|
||||
|
||||
var v = vec2(10, 20)
|
||||
mut v = vec2(10, 20)
|
||||
length(v)
|
||||
|
||||
// can be called using method syntax
|
||||
v.length()
|
||||
|
||||
// function pointers
|
||||
var my_func = fma // deduced type (f32, f32, f32) -> f32
|
||||
mut my_func = fma // deduced type (f32, f32, f32) -> f32
|
||||
|
||||
// pass by reference/pointer with *
|
||||
// TODO: const pointer?
|
||||
func my_system(event: events.update, position: *components.position, velocity: *components.velocity):
|
||||
position += event.dt * velocity
|
||||
func my_system(event: events.update, position: components.position mut*, velocity: components.velocity*) {
|
||||
position += event.dt * velocity
|
||||
}
|
||||
|
||||
func attach(dispatcher: *ecs.dispatcher):
|
||||
// TODO: how does it work? C++-style variadic templates? Oh no...
|
||||
dispatcher.system(my_system)
|
||||
func attach(dispatcher: ecs.dispatcher*) {
|
||||
// TODO: how does it work? C++-style variadic templates? Oh no...
|
||||
dispatcher.system(my_system)
|
||||
}
|
||||
|
||||
// objects with methods
|
||||
struct rectangle:
|
||||
width: i32
|
||||
height: i32
|
||||
struct rectangle {
|
||||
width: i32
|
||||
height: i32
|
||||
}
|
||||
|
||||
func extend(r: &rectangle, size: i32):
|
||||
r.width += size
|
||||
r.height += size
|
||||
func extend(r: rectangle mut*, size: i32) {
|
||||
r.width += size
|
||||
r.height += size
|
||||
}
|
||||
|
||||
var r = rectangle(10, 12)
|
||||
mut r = rectangle(10, 12)
|
||||
r.extend(5)
|
||||
|
||||
// named initializers
|
||||
var r2 = rectangle(width = 20, height = 30)
|
||||
mut r2 = rectangle(width = 20, height = 30)
|
||||
|
||||
// regular pointers
|
||||
var ptr: *i32 = null
|
||||
var x = 15
|
||||
mut ptr: i32* = null
|
||||
mut x = 15
|
||||
ptr = &x
|
||||
|
||||
// field/method access using pointers is the same as with values
|
||||
var sptr: *rectangle = &r
|
||||
mut sptr: rectangle mut* = &r
|
||||
r.width *= 2
|
||||
|
||||
// simple generics
|
||||
struct array(T):
|
||||
data: *T
|
||||
size: u64
|
||||
struct array(T) {
|
||||
data: *T
|
||||
size: u64
|
||||
}
|
||||
|
||||
// TODO: constructors? destructors?
|
||||
func new(self: array(T), size: u64):
|
||||
return array(T)(data = mem.alloc(size * sizeof(T)), size = size)
|
||||
func new(self: array(T), size: u64) {
|
||||
return array(T)(data = mem.alloc(size * sizeof(T)), size = size)
|
||||
}
|
||||
|
||||
// TODO: static arrays?
|
||||
// TODO: move-only types? alloc returns smth like unique ptr?
|
||||
|
||||
struct kvpair(K, V):
|
||||
key: K
|
||||
value: V
|
||||
struct kvpair(K, V) {
|
||||
key: K
|
||||
value: V
|
||||
}
|
||||
|
||||
struct arraymap(K, V):
|
||||
values: array(kvpair(K, V))
|
||||
struct arraymap(K, V) {
|
||||
values: array(kvpair(K, V))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
foreign func sin(x: f64) -> f64
|
||||
foreign func cos(x: f64) -> f64
|
||||
|
||||
func test(x: f64) -> f64:
|
||||
func test(x: f64) -> f64 {
|
||||
let s = sin(x)
|
||||
let c = cos(x)
|
||||
return s * s + c * c
|
||||
}
|
||||
|
|
@ -1,40 +1,48 @@
|
|||
func print(c: u8):
|
||||
func print(c: u8) {
|
||||
foreign func putchar(c: i32) -> i32
|
||||
putchar(c as i32)
|
||||
}
|
||||
|
||||
func print_i32(x: i32):
|
||||
if x < 0:
|
||||
func print_i32(x: i32) {
|
||||
if x < 0 {
|
||||
print('-')
|
||||
print_i32(-x)
|
||||
return
|
||||
if x >= 10:
|
||||
}
|
||||
if x >= 10 {
|
||||
print_i32(x / 10)
|
||||
}
|
||||
print('0' + (x % 10 as u8))
|
||||
}
|
||||
|
||||
func print_f32(x: f32):
|
||||
if x < 0.0:
|
||||
func print_f32(x: f32) {
|
||||
if x < 0.0 {
|
||||
print('-')
|
||||
print_f32(-x)
|
||||
return
|
||||
}
|
||||
foreign func floorf(x: f32) -> f32
|
||||
let floor = floorf(x) as i32
|
||||
print_i32(floor)
|
||||
print('.')
|
||||
mut y = x - (floor as f32)
|
||||
mut i = 0
|
||||
while i < 5:
|
||||
while i < 5 {
|
||||
y = y * 10.0
|
||||
let yfloor = floorf(y) as i32
|
||||
print('0' + (yfloor as u8))
|
||||
y = y - (yfloor as f32)
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
struct vec3:
|
||||
struct vec3 {
|
||||
x: f32
|
||||
y: f32
|
||||
z: f32
|
||||
}
|
||||
|
||||
func print_vec3(v: vec3):
|
||||
func print_vec3(v: vec3) {
|
||||
print('(')
|
||||
print_f32(v.x)
|
||||
print(',')
|
||||
|
|
@ -42,31 +50,39 @@ func print_vec3(v: vec3):
|
|||
print(',')
|
||||
print_f32(v.z)
|
||||
print(')')
|
||||
}
|
||||
|
||||
func dot(a: vec3, b: vec3) -> f32:
|
||||
func dot(a: vec3, b: vec3) -> f32 {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z
|
||||
}
|
||||
|
||||
func add(a: vec3, b: vec3) -> vec3:
|
||||
func add(a: vec3, b: vec3) -> vec3 {
|
||||
return vec3(a.x + b.x, a.y + b.y, a.z + b.z)
|
||||
}
|
||||
|
||||
func mult(a: vec3, b: f32) -> vec3:
|
||||
func mult(a: vec3, b: f32) -> vec3 {
|
||||
return vec3(a.x * b, a.y * b, a.z * b)
|
||||
}
|
||||
|
||||
func normalized(v: vec3) -> vec3:
|
||||
func normalized(v: vec3) -> vec3 {
|
||||
foreign func sqrtf(x: f32) -> f32
|
||||
return mult(v, 1.0 / sqrtf(dot(v, v)))
|
||||
}
|
||||
|
||||
struct ray:
|
||||
struct ray {
|
||||
origin: vec3
|
||||
direction: vec3
|
||||
}
|
||||
|
||||
func intersect_plane(ray: ray, normal: vec3, value: f32) -> f32:
|
||||
func intersect_plane(ray: ray, normal: vec3, value: f32) -> f32 {
|
||||
return (value - dot(ray.origin, normal)) / dot(ray.direction, normal)
|
||||
}
|
||||
|
||||
func test() -> i32[3]:
|
||||
func test() -> i32[3] {
|
||||
return [70, 60, 50]
|
||||
}
|
||||
|
||||
func print_i32_3(a: i32[3]):
|
||||
func print_i32_3(a: i32[3]) {
|
||||
print('[')
|
||||
print_i32(a[0])
|
||||
print(',')
|
||||
|
|
@ -74,6 +90,7 @@ func print_i32_3(a: i32[3]):
|
|||
print(',')
|
||||
print_i32(a[2])
|
||||
print(']')
|
||||
}
|
||||
|
||||
mut a = [1, 2, 3]
|
||||
a = test()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
func print(c: u8):
|
||||
func print(c: u8) {
|
||||
foreign func putchar(c: i32) -> i32
|
||||
putchar(c as i32)
|
||||
}
|
||||
|
||||
func test() -> i32:
|
||||
func test() -> i32 {
|
||||
global mut x = 0
|
||||
x += 1
|
||||
return x
|
||||
}
|
||||
|
||||
print('0' + (test() as u8))
|
||||
print('0' + (test() as u8))
|
||||
|
|
|
|||
|
|
@ -1,32 +1,37 @@
|
|||
foreign func putchar(c: i32) -> i32
|
||||
|
||||
func print(c: u8):
|
||||
func print(c: u8) {
|
||||
foreign func putchar(c: i32) -> i32
|
||||
putchar(c as i32)
|
||||
}
|
||||
|
||||
func mandelbrot():
|
||||
func mandelbrot() {
|
||||
let width = 120
|
||||
let height = 40
|
||||
mut y = 0
|
||||
while y < height:
|
||||
while y < height {
|
||||
mut x = 0
|
||||
while x < width:
|
||||
while x < width {
|
||||
let cx = (x as f32 + 0.5) / (width as f32) * 2.5 - 2.0
|
||||
let cy = (y as f32 + 0.5) / (height as f32) * 2.0 - 1.0
|
||||
mut tx = 0.0
|
||||
mut ty = 0.0
|
||||
mut i = 0
|
||||
while i < 100 && (tx * tx + ty * ty < 4.0):
|
||||
while i < 100 && (tx * tx + ty * ty < 4.0) {
|
||||
let newx = tx * tx - ty * ty + cx
|
||||
ty = 2.0 * tx * ty + cy
|
||||
tx = newx
|
||||
i = i + 1
|
||||
if i == 100:
|
||||
}
|
||||
if i == 100 {
|
||||
print('X')
|
||||
else:
|
||||
} else {
|
||||
print(' ')
|
||||
}
|
||||
|
||||
x = x + 1
|
||||
}
|
||||
y = y + 1
|
||||
print('\n')
|
||||
}
|
||||
}
|
||||
|
||||
mandelbrot()
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,15 @@
|
|||
func g() -> u16:
|
||||
func g() -> u16 {
|
||||
return g()
|
||||
}
|
||||
|
||||
func h() -> bool:
|
||||
func h() -> bool {
|
||||
return h()
|
||||
}
|
||||
|
||||
func test_and() -> u16:
|
||||
func test_and() -> u16 {
|
||||
return 0us && g()
|
||||
}
|
||||
|
||||
func test_or() -> u16:
|
||||
func test_or() -> u16 {
|
||||
return 65535us || g()
|
||||
}
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
struct vec2f:
|
||||
struct vec2f {
|
||||
x: f32
|
||||
y: f32
|
||||
}
|
||||
|
||||
struct body:
|
||||
struct body {
|
||||
position: vec2f
|
||||
rotation: f32
|
||||
}
|
||||
|
||||
func move_x(b: body mut*, delta: f32):
|
||||
(*b).position.x = (*b).position.x + delta
|
||||
func move_x(b: body mut*, delta: f32) {
|
||||
b.position.x = b.position.x + delta
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
// Vectors
|
||||
|
||||
struct vec2:
|
||||
struct vec2 {
|
||||
x : f32
|
||||
y : f32
|
||||
}
|
||||
|
||||
func add(a : vec2, b : vec2) -> vec2:
|
||||
func add(a : vec2, b : vec2) -> vec2 {
|
||||
return vec2(a.x + b.x, a.y + b.y)
|
||||
}
|
||||
|
||||
mut v = add(vec2(1.0, 2.0), vec2(3.0, 4.0))
|
||||
v.x = -v.x
|
||||
|
|
@ -13,34 +15,39 @@ v.y = -v.y
|
|||
|
||||
// Factorial
|
||||
|
||||
func factorial(n : u32) -> u32:
|
||||
if n == 0u:
|
||||
return 1u
|
||||
func factorial(n : u32) -> u32 {
|
||||
if n == 0u { return 1u }
|
||||
return n * factorial(n - 1u)
|
||||
}
|
||||
|
||||
let factorial10 = factorial(10u)
|
||||
|
||||
// Fibonacci
|
||||
func fib(n : u32) -> u32:
|
||||
func fib(n : u32) -> u32 {
|
||||
// Slow implementation with
|
||||
// exponentially-growing recursion tree
|
||||
if n == 0u | n == 1u:
|
||||
return n // base case
|
||||
if n == 0u | n == 1u {
|
||||
// Base case
|
||||
return n
|
||||
}
|
||||
return fib(n - 1u) + fib(n - 2u)
|
||||
}
|
||||
|
||||
let fib10 = fib(10u)
|
||||
|
||||
|
||||
func h() -> u32:
|
||||
func h() -> u32 {
|
||||
return 0u
|
||||
}
|
||||
|
||||
func f() -> u32:
|
||||
func f() -> u32 {
|
||||
return h()
|
||||
}
|
||||
|
||||
func g() -> u32:
|
||||
func h() -> u32:
|
||||
return 1u
|
||||
func g() -> u32 {
|
||||
func h() -> u32 { return 1u }
|
||||
return f()
|
||||
}
|
||||
|
||||
// Should equal 0u, but equals 1u due to an error in
|
||||
// how the interpreter resolves functions & variables
|
||||
|
|
|
|||
|
|
@ -20,29 +20,6 @@ namespace pslang::ast
|
|||
ast::location location;
|
||||
};
|
||||
|
||||
using pre_statement_impl = std::variant<
|
||||
expression_ptr,
|
||||
assignment,
|
||||
variable_declaration,
|
||||
if_block,
|
||||
else_block,
|
||||
else_if_block,
|
||||
while_block,
|
||||
break_statement,
|
||||
continue_statement,
|
||||
function_definition,
|
||||
foreign_function_declaration,
|
||||
return_statement,
|
||||
field_definition,
|
||||
struct_definition
|
||||
>;
|
||||
|
||||
struct pre_statement
|
||||
: pre_statement_impl
|
||||
{
|
||||
using pre_statement_impl::pre_statement_impl;
|
||||
};
|
||||
|
||||
using statement_impl = std::variant<
|
||||
expression_ptr,
|
||||
assignment,
|
||||
|
|
@ -63,7 +40,6 @@ namespace pslang::ast
|
|||
using statement_impl::statement_impl;
|
||||
};
|
||||
|
||||
location get_location(pre_statement const & statement);
|
||||
location get_location(statement const & statement);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,23 +6,15 @@
|
|||
namespace pslang::ast
|
||||
{
|
||||
|
||||
struct pre_statement;
|
||||
struct statement;
|
||||
|
||||
using pre_statement_ptr = std::shared_ptr<pre_statement>;
|
||||
using statement_ptr = std::shared_ptr<statement>;
|
||||
|
||||
struct pre_statement_list
|
||||
{
|
||||
std::vector<pre_statement_ptr> statements;
|
||||
};
|
||||
|
||||
struct statement_list
|
||||
{
|
||||
std::vector<statement_ptr> statements;
|
||||
};
|
||||
|
||||
using pre_statement_list_ptr = std::shared_ptr<pre_statement_list>;
|
||||
using statement_list_ptr = std::shared_ptr<statement_list>;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,6 @@ namespace pslang::ast
|
|||
|
||||
}
|
||||
|
||||
location get_location(pre_statement const & statement)
|
||||
{
|
||||
return std::visit(get_location_visitor{}, statement);
|
||||
}
|
||||
|
||||
location get_location(statement const & statement)
|
||||
{
|
||||
return std::visit(get_location_visitor{}, statement);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/parser/indented_statement.hpp>
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
|
||||
namespace pslang::ast
|
||||
{
|
||||
|
|
@ -15,7 +15,7 @@ namespace pslang::parser
|
|||
struct context
|
||||
{
|
||||
ast::location & location;
|
||||
indented_statement_list & result;
|
||||
ast::statement_list_ptr & result;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
10
libs/parser/include/pslang/parser/finalize.hpp
Normal file
10
libs/parser/include/pslang/parser/finalize.hpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
|
||||
namespace pslang::parser
|
||||
{
|
||||
|
||||
ast::statement_list_ptr finalize(ast::statement_list_ptr statements);
|
||||
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace pslang::parser
|
||||
{
|
||||
|
||||
struct indented_statement
|
||||
{
|
||||
std::size_t indentation;
|
||||
ast::pre_statement_ptr statement;
|
||||
};
|
||||
|
||||
struct indented_statement_list
|
||||
{
|
||||
std::vector<indented_statement> statements;
|
||||
};
|
||||
|
||||
ast::statement_list_ptr finalize(indented_statement_list statements);
|
||||
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ using bp = ::pslang::parser::bison::parser;
|
|||
ctx.location.step();
|
||||
%}
|
||||
|
||||
[ ]+ { ctx.location.step(); }
|
||||
[ \r\t]+ { ctx.location.step(); }
|
||||
|
||||
"//"[^\n]* { return bp::make_comment(ctx.location); }
|
||||
|
||||
|
|
@ -60,7 +60,6 @@ f64 { return bp::make_f64(ctx.location); }
|
|||
[a-zA-Z_]+[a-zA-Z0-9_]* { return bp::make_name(yytext, ctx.location); }
|
||||
|
||||
"\n" { auto old_location = ctx.location; ctx.location.move_lines(1); return bp::make_newline(old_location); }
|
||||
"\t" { return bp::make_indent(ctx.location); }
|
||||
"=" { return bp::make_assignment(ctx.location); }
|
||||
":" { return bp::make_colon(ctx.location); }
|
||||
"," { return bp::make_comma(ctx.location); }
|
||||
|
|
@ -69,6 +68,8 @@ f64 { return bp::make_f64(ctx.location); }
|
|||
")" { return bp::make_rparen(ctx.location); }
|
||||
"[" { return bp::make_lbracket(ctx.location); }
|
||||
"]" { return bp::make_rbracket(ctx.location); }
|
||||
"{" { return bp::make_lbrace(ctx.location); }
|
||||
"}" { return bp::make_rbrace(ctx.location); }
|
||||
"+=" { return bp::make_plus_assignment(ctx.location); }
|
||||
"-=" { return bp::make_minus_assignment(ctx.location); }
|
||||
"*=" { return bp::make_asterisk_assignment(ctx.location); }
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
%code requires {
|
||||
|
||||
#include <pslang/parser/indented_statement.hpp>
|
||||
#include <pslang/ast/statement.hpp>
|
||||
|
||||
namespace pslang::parser {
|
||||
|
|
@ -75,8 +74,7 @@ template <typename T>
|
|||
%define api.token.prefix {tok_}
|
||||
|
||||
%token newline "newline"
|
||||
%token indent "indentation"
|
||||
%token comment
|
||||
%token comment "comment"
|
||||
%token assignment "="
|
||||
%token colon ":"
|
||||
%token comma ","
|
||||
|
|
@ -85,6 +83,8 @@ template <typename T>
|
|||
%token rparen ")"
|
||||
%token lbracket "["
|
||||
%token rbracket "]"
|
||||
%token lbrace "{"
|
||||
%token rbrace "}"
|
||||
%token plus "+"
|
||||
%token minus "-"
|
||||
%token asterisk "*"
|
||||
|
|
@ -184,10 +184,12 @@ template <typename T>
|
|||
%precedence else
|
||||
%precedence lbracket
|
||||
|
||||
%type <indented_statement_list> indented_statement_list
|
||||
%type <indented_statement> statement_line
|
||||
%type <std::size_t> indentation
|
||||
%type <ast::pre_statement> statement
|
||||
%type <ast::statement_list> statement_list
|
||||
%type <ast::statement_list_ptr> statement_block
|
||||
%type <ast::statement> statement_line
|
||||
%type <ast::statement> statement
|
||||
%type <ast::if_chain> if_chain
|
||||
%type <ast::if_chain::block> single_if
|
||||
%type <std::vector<ast::function_declaration::argument>> function_declaration_argument_list
|
||||
%type <std::vector<ast::function_declaration::argument>> nonempty_function_declaration_argument_list
|
||||
%type <ast::function_declaration::argument> function_declaration_single_argument
|
||||
|
|
@ -199,6 +201,8 @@ template <typename T>
|
|||
%type <types::primitive_type> primitive_type
|
||||
%type <std::vector<ast::type_ptr>> function_paren_type_list
|
||||
%type <std::vector<ast::type_ptr>> two_or_more_type_list
|
||||
%type <std::vector<ast::field_definition>> field_definition_list
|
||||
%type <ast::field_definition> field_definition
|
||||
%type <ast::expression> expression
|
||||
%type <ast::expression> postfix_expression
|
||||
%type <ast::expression> base_expression
|
||||
|
|
@ -209,27 +213,26 @@ template <typename T>
|
|||
%%
|
||||
|
||||
module
|
||||
: indented_statement_list end { ctx.result = $1; }
|
||||
: statement_list end { ctx.result = std::make_unique<ast::statement_list>($1); }
|
||||
;
|
||||
|
||||
indented_statement_list
|
||||
: statement_line { indented_statement_list tmp; tmp.statements.push_back(std::move($1)); $$ = std::move(tmp); }
|
||||
statement_list
|
||||
: statement_line { ast::statement_list tmp; tmp.statements.push_back(std::make_unique<ast::statement>($1)); $$ = std::move(tmp); }
|
||||
| empty_line { $$ = {}; }
|
||||
| indented_statement_list newline statement_line { auto tmp = $1; tmp.statements.push_back(std::move($3)); $$ = std::move(tmp); }
|
||||
| indented_statement_list newline empty_line { $$ = $1; }
|
||||
| statement_list newline statement_line { auto tmp = $1; tmp.statements.push_back(std::make_unique<ast::statement>($3)); $$ = std::move(tmp); }
|
||||
| statement_list newline empty_line { $$ = $1; }
|
||||
;
|
||||
|
||||
statement_block
|
||||
: lbrace statement_list rbrace { $$ = std::make_unique<ast::statement_list>($2); }
|
||||
;
|
||||
|
||||
statement_line
|
||||
: indentation statement optional_comment { $$ = indented_statement{$1, std::make_unique<ast::pre_statement>($2)}; }
|
||||
: statement optional_comment { $$ = $1; }
|
||||
;
|
||||
|
||||
empty_line
|
||||
: indentation optional_comment
|
||||
;
|
||||
|
||||
indentation
|
||||
: indent indentation { $$ = $2 + 1ull; }
|
||||
| %empty { $$ = 0ull; }
|
||||
: optional_comment
|
||||
;
|
||||
|
||||
optional_comment
|
||||
|
|
@ -251,18 +254,25 @@ statement
|
|||
| expression left_shift_assignment expression { auto lhs = std::make_shared<ast::expression>($1); $$ = ast::assignment{ lhs, std::make_shared<ast::expression>(ast::binary_operation{ ast::binary_operation_type::left_shift, lhs, std::make_unique<ast::expression>($3), @$ }), @$ }; }
|
||||
| expression right_shift_assignment expression { auto lhs = std::make_shared<ast::expression>($1); $$ = ast::assignment{ lhs, std::make_shared<ast::expression>(ast::binary_operation{ ast::binary_operation_type::right_shift, lhs, std::make_unique<ast::expression>($3), @$ }), @$ }; }
|
||||
| variable_declaration { $$ = $1; }
|
||||
| if expression colon { $$ = ast::if_block{std::make_unique<ast::expression>($2), @$}; }
|
||||
| else colon { $$ = ast::else_block{@$}; }
|
||||
| else if expression colon { $$ = ast::else_if_block{std::make_unique<ast::expression>($3), @$}; }
|
||||
| while expression colon { $$ = ast::while_block{std::make_unique<ast::expression>($2), {}, @$, @$}; }
|
||||
| if_chain { $$ = $1; }
|
||||
| while expression statement_block { $$ = ast::while_block{std::make_unique<ast::expression>($2), $3, @$, @$}; }
|
||||
| break { $$ = ast::break_statement{@$}; }
|
||||
| continue { $$ = ast::continue_statement{@$}; }
|
||||
| func name lparen function_declaration_argument_list rparen function_return_type colon { $$ = ast::function_definition{{$2, $4, $6, @$}, {}}; }
|
||||
| func name lparen function_declaration_argument_list rparen function_return_type statement_block { $$ = ast::function_definition{{$2, $4, $6, @$}, $7}; }
|
||||
| foreign func name lparen function_declaration_argument_list rparen function_return_type { $$ = ast::foreign_function_declaration{{$3, $5, $7, @$}}; }
|
||||
| return expression { $$ = ast::return_statement{std::make_unique<ast::expression>($2), @$}; }
|
||||
| return { $$ = ast::return_statement{nullptr, @$}; }
|
||||
| struct name colon { $$ = ast::struct_definition{$2, {}, @$, @$}; }
|
||||
| name colon type_expression { $$ = ast::field_definition{$1, std::make_unique<ast::type>($3), @$}; }
|
||||
| struct name lbrace field_definition_list rbrace { $$ = ast::struct_definition{$2, $4, merge(@1, @2), @$}; }
|
||||
;
|
||||
|
||||
if_chain
|
||||
: single_if { $$ = ast::if_chain{{$1}, @$}; }
|
||||
| if_chain else single_if { auto tmp = $1; tmp.blocks.push_back($3); tmp.location = @$; $$ = std::move(tmp); }
|
||||
| if_chain else statement_block { auto tmp = $1; tmp.blocks.push_back({nullptr, $3, @2, merge(@2, @3)}); tmp.location = @$; $$ = std::move(tmp); }
|
||||
;
|
||||
|
||||
single_if
|
||||
: if expression statement_block { $$ = ast::if_chain::block{std::make_unique<ast::expression>($2), $3, merge(@1, @2), @$}; }
|
||||
;
|
||||
|
||||
function_declaration_argument_list
|
||||
|
|
@ -301,6 +311,17 @@ variable_keyword
|
|||
| mut { $$ = ast::value_category::_mutable; }
|
||||
;
|
||||
|
||||
field_definition_list
|
||||
: field_definition { std::vector<ast::field_definition> tmp; tmp.push_back(std::move($1)); $$ = std::move(tmp); }
|
||||
| empty_line { $$ = {}; }
|
||||
| field_definition_list newline field_definition { auto tmp = $1; tmp.push_back(std::move($3)); $$ = std::move(tmp); }
|
||||
| field_definition_list newline empty_line { $$ = $1; }
|
||||
;
|
||||
|
||||
field_definition
|
||||
: name colon type_expression { $$ = ast::field_definition{$1, std::make_unique<ast::type>($3), @$}; }
|
||||
;
|
||||
|
||||
type_expression
|
||||
: unit { $$ = types::unit_type{}; }
|
||||
| primitive_type { $$ = ast::type($1); }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include <pslang/parser/indented_statement.hpp>
|
||||
#include <pslang/parser/finalize.hpp>
|
||||
#include <pslang/parser/error.hpp>
|
||||
#include <pslang/ast/statement.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
|
|
@ -11,265 +11,50 @@ namespace pslang::parser
|
|||
namespace
|
||||
{
|
||||
|
||||
struct fill_location_visitor
|
||||
: ast::statement_visitor<fill_location_visitor>
|
||||
void validate(ast::statement_list_ptr statements, ast::function_definition * in_function, bool in_loop)
|
||||
{
|
||||
using statement_visitor::apply;
|
||||
|
||||
ast::location apply(ast::expression_ptr const & node)
|
||||
for (auto & statement : statements->statements)
|
||||
{
|
||||
return ast::get_location(*node);
|
||||
}
|
||||
|
||||
ast::location apply(ast::assignment const & node)
|
||||
{
|
||||
return node.location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::variable_declaration const & node)
|
||||
{
|
||||
return node.location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::if_chain & node)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto & block : node.blocks)
|
||||
if (auto if_chain = std::get_if<ast::if_chain>(statement.get()))
|
||||
{
|
||||
block.location = apply(*block.statements);
|
||||
if (first)
|
||||
node.location = block.location;
|
||||
else
|
||||
node.location = ast::merge(node.location, block.location);
|
||||
first = false;
|
||||
for (auto const & block : if_chain->blocks)
|
||||
{
|
||||
validate(block.statements, in_function, in_loop);
|
||||
}
|
||||
}
|
||||
return node.location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::while_block & node)
|
||||
{
|
||||
return node.location = ast::merge(node.prelude_location, apply(*node.statements));
|
||||
}
|
||||
|
||||
ast::location apply(ast::break_statement & node)
|
||||
{
|
||||
return node.location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::continue_statement & node)
|
||||
{
|
||||
return node.location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::function_definition & node)
|
||||
{
|
||||
return node.location = ast::merge(node.prelude_location, apply(*node.statements));
|
||||
}
|
||||
|
||||
ast::location apply(ast::foreign_function_declaration & node)
|
||||
{
|
||||
return node.location = node.prelude_location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::return_statement const & node)
|
||||
{
|
||||
return node.location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::struct_definition & node)
|
||||
{
|
||||
node.location = node.prelude_location;
|
||||
for (auto const & field : node.fields)
|
||||
node.location = ast::merge(node.location, field.location);
|
||||
return node.location;
|
||||
}
|
||||
|
||||
ast::location apply(ast::statement_list & list)
|
||||
{
|
||||
ast::location result;
|
||||
bool first = true;
|
||||
for (auto & statement : list.statements)
|
||||
else if (auto while_block = std::get_if<ast::while_block>(statement.get()))
|
||||
{
|
||||
auto statement_location = apply(*statement);
|
||||
if (first)
|
||||
result = statement_location;
|
||||
else
|
||||
result = ast::merge(result, statement_location);
|
||||
first = false;
|
||||
validate(while_block->statements, in_function, true);
|
||||
}
|
||||
else if (auto function_definition = std::get_if<ast::function_definition>(statement.get()))
|
||||
{
|
||||
validate(function_definition->statements, function_definition, in_loop);
|
||||
}
|
||||
else if (auto return_statement = std::get_if<ast::return_statement>(statement.get()))
|
||||
{
|
||||
if (!in_function)
|
||||
throw parse_error("Return statement outside of function scope", return_statement->location);
|
||||
return_statement->node = in_function;
|
||||
}
|
||||
else if (auto break_statement = std::get_if<ast::break_statement>(statement.get()))
|
||||
{
|
||||
if (!in_loop)
|
||||
throw parse_error("Break without an enclosing loop", break_statement->location);
|
||||
}
|
||||
else if (auto continue_statement = std::get_if<ast::continue_statement>(statement.get()))
|
||||
{
|
||||
if (!in_loop)
|
||||
throw parse_error("Continue without an enclosing loop", continue_statement->location);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
ast::statement_list_ptr finalize(indented_statement_list statements)
|
||||
{
|
||||
ast::statement_list_ptr result = std::make_unique<ast::statement_list>();
|
||||
|
||||
using stack_entry = std::variant<ast::statement_list *, ast::struct_definition *>;
|
||||
|
||||
std::vector<stack_entry> stack;
|
||||
stack.push_back(result.get());
|
||||
std::size_t current_indent = 0;
|
||||
std::vector<ast::function_definition *> function_stack;
|
||||
std::vector<ast::statement_list *> loop_stack;
|
||||
|
||||
auto current_statement_list = [&](ast::location const & location) -> ast::statement_list *
|
||||
{
|
||||
if (stack.empty())
|
||||
throw internal_error("Empty finilization stack");
|
||||
|
||||
if (auto list = std::get_if<ast::statement_list *>(&stack.back()))
|
||||
return *list;
|
||||
|
||||
throw parse_error("Unexpected statement inside struct definition", location);
|
||||
};
|
||||
|
||||
auto current_struct_definition = [&](ast::location const & location) -> ast::struct_definition *
|
||||
{
|
||||
if (stack.empty())
|
||||
throw internal_error("Empty finilization stack");
|
||||
|
||||
if (auto list = std::get_if<ast::struct_definition *>(&stack.back()))
|
||||
return *list;
|
||||
|
||||
throw parse_error("Unexpected statement outside struct definition", location);
|
||||
};
|
||||
|
||||
for (auto & statement : statements.statements)
|
||||
{
|
||||
auto location = ast::get_location(*statement.statement);
|
||||
|
||||
if (statement.indentation > current_indent)
|
||||
throw parse_error("Unexpected indent", location);
|
||||
|
||||
while (statement.indentation < current_indent)
|
||||
{
|
||||
if (stack.empty())
|
||||
throw ast::invalid_ast_error("Unexpected empty indent stack", ast::get_location(*statement.statement));
|
||||
if (!function_stack.empty() && std::holds_alternative<ast::statement_list *>(stack.back()) && function_stack.back()->statements.get() == std::get<ast::statement_list *>(stack.back()))
|
||||
function_stack.pop_back();
|
||||
if (!loop_stack.empty() && std::holds_alternative<ast::statement_list *>(stack.back()) && loop_stack.back() == std::get<ast::statement_list *>(stack.back()))
|
||||
loop_stack.pop_back();
|
||||
stack.pop_back();
|
||||
--current_indent;
|
||||
}
|
||||
|
||||
// Now statement.indentation == current_indent
|
||||
|
||||
ast::statement_list * list = nullptr;
|
||||
|
||||
if (auto if_block = std::get_if<ast::if_block>(statement.statement.get()))
|
||||
{
|
||||
ast::if_chain chain;
|
||||
chain.location = if_block->location;
|
||||
chain.blocks.push_back({.condition = std::move(if_block->condition), .statements = std::make_unique<ast::statement_list>(), .prelude_location = if_block->location});
|
||||
list = chain.blocks.back().statements.get();
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(chain)));
|
||||
}
|
||||
else if (auto else_block = std::get_if<ast::else_block>(statement.statement.get()))
|
||||
{
|
||||
if (current_statement_list(location)->statements.empty())
|
||||
throw parse_error("Unexpected else block", location);
|
||||
auto chain = std::get_if<ast::if_chain>(current_statement_list(location)->statements.back().get());
|
||||
if (!chain || chain->blocks.empty() || !chain->blocks.back().condition)
|
||||
throw parse_error("Unexpected else block", location);
|
||||
|
||||
chain->blocks.push_back({.condition = nullptr, .statements = std::make_unique<ast::statement_list>(), .prelude_location = else_block->location});
|
||||
list = chain->blocks.back().statements.get();
|
||||
}
|
||||
else if (auto else_if_block = std::get_if<ast::else_if_block>(statement.statement.get()))
|
||||
{
|
||||
if (current_statement_list(location)->statements.empty())
|
||||
throw parse_error("Unexpected else if block", location);
|
||||
auto chain = std::get_if<ast::if_chain>(current_statement_list(location)->statements.back().get());
|
||||
if (!chain || chain->blocks.empty() || !chain->blocks.back().condition)
|
||||
throw parse_error("Unexpected else if block", location);
|
||||
|
||||
chain->blocks.push_back({.condition = std::move(else_if_block->condition), .statements = std::make_unique<ast::statement_list>(), .prelude_location = else_if_block->location});
|
||||
list = chain->blocks.back().statements.get();
|
||||
}
|
||||
else if (auto while_block = std::get_if<ast::while_block>(statement.statement.get()))
|
||||
{
|
||||
while_block->statements = std::make_unique<ast::statement_list>();
|
||||
list = while_block->statements.get();
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*while_block)));
|
||||
loop_stack.push_back(list);
|
||||
}
|
||||
else if (auto function_definition = std::get_if<ast::function_definition>(statement.statement.get()))
|
||||
{
|
||||
function_definition->statements = std::make_unique<ast::statement_list>();
|
||||
auto statement = std::make_unique<ast::statement>(std::move(*function_definition));
|
||||
auto function_definition_ptr = std::get_if<ast::function_definition>(statement.get());
|
||||
current_statement_list(location)->statements.push_back(std::move(statement));
|
||||
list = function_definition_ptr->statements.get();
|
||||
function_stack.push_back(function_definition_ptr);
|
||||
}
|
||||
else if (auto field_definition = std::get_if<ast::field_definition>(statement.statement.get()))
|
||||
{
|
||||
auto current = current_struct_definition(location);
|
||||
for (auto const & field : current->fields)
|
||||
if (field.name == field_definition->name)
|
||||
throw parse_error("Duplicate field definition: \"" + field.name + "\"", field.location);
|
||||
current->fields.push_back(*field_definition);
|
||||
}
|
||||
else if (auto struct_definition = std::get_if<ast::struct_definition>(statement.statement.get()))
|
||||
{
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*struct_definition)));
|
||||
stack.push_back(std::get_if<ast::struct_definition>(current_statement_list(location)->statements.back().get()));
|
||||
++current_indent;
|
||||
}
|
||||
else if (auto return_statement = std::get_if<ast::return_statement>(statement.statement.get()))
|
||||
{
|
||||
if (function_stack.empty())
|
||||
throw parse_error("Return statement outside of function scope", return_statement->location);
|
||||
return_statement->node = function_stack.back();
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*return_statement)));
|
||||
}
|
||||
else if (auto expression_ptr = std::get_if<ast::expression_ptr>(statement.statement.get()))
|
||||
{
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*expression_ptr)));
|
||||
}
|
||||
else if (auto assignment = std::get_if<ast::assignment>(statement.statement.get()))
|
||||
{
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*assignment)));
|
||||
}
|
||||
else if (auto variable_declaration = std::get_if<ast::variable_declaration>(statement.statement.get()))
|
||||
{
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*variable_declaration)));
|
||||
}
|
||||
else if (auto foreign_function_declaration = std::get_if<ast::foreign_function_declaration>(statement.statement.get()))
|
||||
{
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*foreign_function_declaration)));
|
||||
}
|
||||
else if (auto break_statement = std::get_if<ast::break_statement>(statement.statement.get()))
|
||||
{
|
||||
if (loop_stack.empty())
|
||||
throw parse_error("Break without an enclosing loop", break_statement->location);
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*break_statement)));
|
||||
}
|
||||
else if (auto continue_statement = std::get_if<ast::continue_statement>(statement.statement.get()))
|
||||
{
|
||||
if (loop_stack.empty())
|
||||
throw parse_error("Continue without an enclosing loop", continue_statement->location);
|
||||
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*continue_statement)));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ast::invalid_ast_error(std::format("Unknown pre-statement \"{}\"", std::visit([](auto const & statement){ return typeid(statement).name(); }, *statement.statement)), location);
|
||||
}
|
||||
|
||||
if (list)
|
||||
{
|
||||
stack.push_back(list);
|
||||
++current_indent;
|
||||
}
|
||||
}
|
||||
|
||||
fill_location_visitor{}.apply(*result);
|
||||
}
|
||||
|
||||
return result;
|
||||
ast::statement_list_ptr finalize(ast::statement_list_ptr statements)
|
||||
{
|
||||
validate(statements, nullptr, false);
|
||||
return statements;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include <pslang/parser/parser.hpp>
|
||||
#include <pslang/parser/context.hpp>
|
||||
#include <pslang/parser/indented_statement.hpp>
|
||||
#include <pslang/parser/finalize.hpp>
|
||||
#include <pslang/ast/location.hpp>
|
||||
#include "gen_parser.hpp"
|
||||
#include "gen_lexer.hpp"
|
||||
|
|
@ -15,7 +15,7 @@ namespace pslang::parser
|
|||
throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)));
|
||||
|
||||
ast::location location{.begin = {.filename = path}, .end = {.filename = path}};
|
||||
indented_statement_list statements;
|
||||
ast::statement_list_ptr statements;
|
||||
context ctx{location, statements};
|
||||
|
||||
bison::parser parser(ctx);
|
||||
|
|
|
|||
301
spec.txt
301
spec.txt
|
|
@ -36,41 +36,41 @@ Function types:
|
|||
======== LITERALS ========
|
||||
|
||||
Literals:
|
||||
56b -> i8
|
||||
42ub -> u8
|
||||
456s -> i16
|
||||
456us -> u16
|
||||
98765 -> i32
|
||||
98765u -> u32
|
||||
123l -> i64
|
||||
123ul -> u64
|
||||
3.14h -> f16
|
||||
3.14 -> f32
|
||||
3.14d -> f64
|
||||
'a' -> u8 (ascii only?)
|
||||
'猫'u -> u32
|
||||
56b -> i8
|
||||
42ub -> u8
|
||||
456s -> i16
|
||||
456us -> u16
|
||||
98765 -> i32
|
||||
98765u -> u32
|
||||
123l -> i64
|
||||
123ul -> u64
|
||||
3.14h -> f16
|
||||
3.14 -> f32
|
||||
3.14d -> f64
|
||||
'a' -> u8 (ascii only?)
|
||||
'猫'u -> u32
|
||||
|
||||
// TODO: string literals? fixed-size arrays? built-in spans? Probably built-in spans (defined in prelude.psl)
|
||||
"hello, world" -> utf-8 string
|
||||
"здарова, братки"u -> utf-32 string
|
||||
"hello, world" -> utf-8 string
|
||||
"здарова, братки"u -> utf-32 string
|
||||
|
||||
======== VARIABLES ========
|
||||
|
||||
Variable declaration:
|
||||
const x = ... compile-time value, type inferred
|
||||
const x: T = ... compile-time value of type T
|
||||
let x = ... immutable value, type inferred
|
||||
let x: T = ... immutable value of type T
|
||||
mut x = ... mutable, ...
|
||||
mut x: T = ...
|
||||
const x = ... compile-time value, type inferred
|
||||
const x: T = ... compile-time value of type T
|
||||
let x = ... immutable value, type inferred
|
||||
let x: T = ... immutable value of type T
|
||||
mut x = ... mutable, ...
|
||||
mut x: T = ...
|
||||
|
||||
Array declaration:
|
||||
let arr: i32[4] = [12, 15, 65, 42]
|
||||
let arr = [2, 5, 6] // size and type inferred as i32[3]
|
||||
let arr: i32[0] = [] // need special empty array literal, type cannot be inferred
|
||||
let arr: i32[4] = [12, 15, 65, 42]
|
||||
let arr = [2, 5, 6] // size and type inferred as i32[3]
|
||||
let arr: i32[0] = [] // need special empty array literal, type cannot be inferred
|
||||
|
||||
Null pointer literal:
|
||||
let p: u32* = null // special like empty array literal, type cannot be inferred
|
||||
let p: u32* = null // special like empty array literal, type cannot be inferred
|
||||
|
||||
Variables must always be initialized. // TODO: really? What about arrays? Maybe need special syntax for zero-initialization or mass-initialization. Alternative: default to zero-initialization
|
||||
Const variables must be initialized with a const expression (any expression that doesn't include non-const values).
|
||||
|
|
@ -78,162 +78,176 @@ Const variables must be initialized with a const expression (any expression that
|
|||
======== OPERATORS ========
|
||||
|
||||
Logical (only bool type):
|
||||
!x
|
||||
x & y
|
||||
x | y
|
||||
x && y // short-circuit
|
||||
x || y // short-circuit
|
||||
x ^ y
|
||||
!x
|
||||
x & y
|
||||
x | y
|
||||
x && y // short-circuit
|
||||
x || y // short-circuit
|
||||
x ^ y
|
||||
|
||||
Equality (all built-in types, all pointer types, all array/struct types, only same type unless integers):
|
||||
x == y
|
||||
x != y
|
||||
x == y
|
||||
x != y
|
||||
|
||||
Comparison (all built-in types, all pointer types, all array/struct types, only same type unless integers):
|
||||
x < y
|
||||
x > y
|
||||
x <= y
|
||||
x >= y
|
||||
x < y
|
||||
x > y
|
||||
x <= y
|
||||
x >= y
|
||||
|
||||
Bitwise (integer types, only same type):
|
||||
!x
|
||||
x & y
|
||||
x | y
|
||||
x && y // short-circuit
|
||||
x || y // short-circuit
|
||||
x ^ y
|
||||
!x
|
||||
x & y
|
||||
x | y
|
||||
x && y // short-circuit
|
||||
x || y // short-circuit
|
||||
x ^ y
|
||||
|
||||
Bitwise shift (any integer + any unsigned integer type):
|
||||
x >> y
|
||||
x << y
|
||||
x >> y
|
||||
x << y
|
||||
|
||||
Arithmetic (only same integer/floating-point type):
|
||||
-x
|
||||
x + y
|
||||
x * y
|
||||
x / y
|
||||
x % y
|
||||
-x
|
||||
x + y
|
||||
x * y
|
||||
x / y
|
||||
x % y
|
||||
|
||||
Pointer arithmetic (any pointer type + any integer type):
|
||||
p + x
|
||||
p - x
|
||||
p - q // returns i64
|
||||
p + x
|
||||
p - x
|
||||
p - q // returns i64
|
||||
|
||||
Pointer arithmetic works element-wise (like C or C++), i.e. p + n advances by n * sizeof(T) when typeof(p) is *T
|
||||
|
||||
Casting:
|
||||
x as u32 // always explicit, no implicit casts allowed
|
||||
x as u32 // always explicit, no implicit casts allowed
|
||||
|
||||
The only implicit casting allowed is T mut* -> T* (maybe?)
|
||||
Any integer/floating-point types can be cast to each other.
|
||||
Any pointer types can be cast to each other // TODO: alignment? UB or safe fallback? Probably UB.
|
||||
|
||||
Ternary if operator:
|
||||
if condition then true_value else false_value
|
||||
if condition then true_value else false_value
|
||||
|
||||
Address:
|
||||
&x // returns *T, fails if x is a const variable
|
||||
&mut x // returns *mut T, fails if x is non-mut variable
|
||||
&x // returns *T, fails if x is a const variable
|
||||
&mut x // returns *mut T, fails if x is non-mut variable
|
||||
|
||||
Assignment:
|
||||
x = 15 // requires x to be a mut variable
|
||||
*p = 15 // p must be a pointer to mut
|
||||
x = 15 // requires x to be a mut variable
|
||||
*p = 15 // p must be a pointer to mut
|
||||
|
||||
Special built-ins:
|
||||
typeof(value)
|
||||
sizeof(type)
|
||||
sizeof(value) // same as sizeof(typeof(value))
|
||||
alignof(type)
|
||||
alignof(value) // same as alignof(typeof(value))
|
||||
offsetof(struct type, field)
|
||||
offsetof(field access expression)
|
||||
typeof(value)
|
||||
sizeof(type)
|
||||
sizeof(value) // same as sizeof(typeof(value))
|
||||
alignof(type)
|
||||
alignof(value) // same as alignof(typeof(value))
|
||||
offsetof(struct type, field)
|
||||
offsetof(field access expression)
|
||||
|
||||
======== FLOW CONTROL ========
|
||||
|
||||
Conditionals:
|
||||
if condition:
|
||||
statements
|
||||
else if condition:
|
||||
statements
|
||||
else:
|
||||
statements
|
||||
if condition {
|
||||
statements
|
||||
} else if condition {
|
||||
statements
|
||||
} else {
|
||||
statements
|
||||
}
|
||||
|
||||
While loop:
|
||||
while condition:
|
||||
statements
|
||||
if x:
|
||||
break
|
||||
if y:
|
||||
continue
|
||||
while condition {
|
||||
statements
|
||||
if x {
|
||||
break
|
||||
}
|
||||
if y {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
Iterator interface:
|
||||
get(it) returns the currently pointed-to value
|
||||
get_ref(it) returns the pointer to the currently pointed-to value
|
||||
next(it) returns the next iterator
|
||||
get(it) returns the currently pointed-to value
|
||||
get_ref(it) returns the pointer to the currently pointed-to value
|
||||
next(it) returns the next iterator
|
||||
|
||||
Range interface:
|
||||
begin(range) returns the begin iterator
|
||||
end(range) returns the end iterator
|
||||
begin(range) returns the begin iterator
|
||||
end(range) returns the end iterator
|
||||
|
||||
For loop:
|
||||
Operates only on ranges.
|
||||
|
||||
for x in range(10):
|
||||
do_something(x)
|
||||
Operates only on ranges.
|
||||
|
||||
for x in range(10) {
|
||||
do_something(x)
|
||||
}
|
||||
|
||||
i is immutable within the loop body.
|
||||
i is immutable within the loop body.
|
||||
|
||||
Modifiable ranges use special syntax for pointers to elements:
|
||||
Modifiable ranges use special syntax for pointers to elements:
|
||||
|
||||
for &x in array:
|
||||
*x += 1
|
||||
for &x in array {
|
||||
*x += 1
|
||||
}
|
||||
|
||||
The loop is equivalent to
|
||||
The loop is equivalent to
|
||||
|
||||
mut begin = begin(range)
|
||||
let end = end(range)
|
||||
while begin != end:
|
||||
let x = get(begin) // or get_ref(x) for pointer loop
|
||||
statements
|
||||
begin = next(begin)
|
||||
mut it = begin(range)
|
||||
let end = end(range)
|
||||
while it != end {
|
||||
let x = get(it) // or get_ref(x) for pointer loop
|
||||
statements
|
||||
it = next(it)
|
||||
}
|
||||
|
||||
The prelude contains an implementation of range interface for built-in arrays.
|
||||
The prelude contains an implementation of range interface for built-in arrays.
|
||||
|
||||
======== STRUCTS ========
|
||||
|
||||
Struct types:
|
||||
struct rect:
|
||||
width: u32
|
||||
height: u32
|
||||
struct rect {
|
||||
width: u32
|
||||
height: u32
|
||||
}
|
||||
|
||||
Creating a struct value:
|
||||
let x = rect(10u, 20u)
|
||||
let y = rect(width = 10u, height = 20u) // named function arguments in general?
|
||||
let x = rect(10u, 20u)
|
||||
let y = rect(width = 10u, height = 20u) // named function arguments in general?
|
||||
|
||||
Struct field access:
|
||||
let r = rect(1u, 2u)
|
||||
let x = r.width
|
||||
let p = &r
|
||||
let y = p.height // field access through pointer is the same
|
||||
let r = rect(1u, 2u)
|
||||
let x = r.width
|
||||
let p = &r
|
||||
let y = p.height // field access through pointer is the same
|
||||
|
||||
// TODO: inner struct functions maybe? to act as namespace/module containers
|
||||
|
||||
======== FUNCTIONS ========
|
||||
|
||||
Function definition:
|
||||
func foo(x: i32, y: i32) -> i32:
|
||||
return x * y
|
||||
func foo(x: i32, y: i32) -> i32 {
|
||||
return x * y
|
||||
}
|
||||
|
||||
func bar(x: f32): // deduced return type unit
|
||||
print(x)
|
||||
func bar(x: f32) { // deduced return type unit
|
||||
print(x)
|
||||
}
|
||||
|
||||
Function arguments are automatically immutable (as if declared with let).
|
||||
Function arguments are immutable by default (as if declared with let) unless declared with mut:
|
||||
|
||||
// External function: name taken literally as `powf`
|
||||
// and C calling convention assumed
|
||||
foreign func powf(x: f32, y: f32) -> f32 // no implementation
|
||||
func bar(mut x: u32) -> u32 {
|
||||
x = (x + 1u)
|
||||
x = x * x
|
||||
return x
|
||||
}
|
||||
|
||||
// TODO: mutable function arguments?
|
||||
// External function: name taken literally as `powf`
|
||||
// and C calling convention assumed
|
||||
foreign func powf(x: f32, y: f32) -> f32 // no implementation
|
||||
|
||||
// TODO: function overloading? Probably requires selecting a specific overload using `as` operator to save to a value (but not on call site)
|
||||
// TODO: alternative - Rust-like traits, aka parametric polymorphism?
|
||||
|
|
@ -265,12 +279,14 @@ Types are also considered to be values. The keyword `type` denotes the type of a
|
|||
I.e. `typeof(16) == i32` and `typeof(i32) == type`. Incidentally, `typeof(type) == type` as well; there are no type kinds or etc.
|
||||
`type` can be used in any place where a type is required (variable types, function arguments, function return value, etc).
|
||||
E.g.
|
||||
func foo(x: type) -> type:
|
||||
return x[4] // type of arrays of 4 elements of type x
|
||||
func foo(x: type) -> type {
|
||||
return x[4] // type of arrays of 4 elements of type x
|
||||
}
|
||||
|
||||
let y: type = u32
|
||||
if foo(y) == u32[4]:
|
||||
do_smth()
|
||||
let y: type = u32
|
||||
if foo(y) == u32[4] {
|
||||
do_smth()
|
||||
}
|
||||
|
||||
======== CONST EXPRESSIONS ========
|
||||
|
||||
|
|
@ -283,12 +299,14 @@ E.g.
|
|||
// Functions returning functions/structs
|
||||
// Syntactic sugar for common cases
|
||||
// Figure out: max(a,b) - how to deduce type parameters? How does it play with overloading?
|
||||
// func max(t: type):
|
||||
// return func(x : t, y : t):
|
||||
// func max(t: type) {
|
||||
// return func(x : t, y : t) {
|
||||
// if x > y:
|
||||
// return x
|
||||
// else:
|
||||
// return y
|
||||
// }
|
||||
// }
|
||||
|
||||
======== PRELUDE ========
|
||||
|
||||
|
|
@ -296,31 +314,32 @@ Prelude is a special source file implicitly included in any project (unless expl
|
|||
|
||||
It contains:
|
||||
|
||||
An array_view template struct:
|
||||
An array_view template struct:
|
||||
|
||||
struct array_view<t: type>:
|
||||
size: u64
|
||||
data: t*
|
||||
struct array_view<t: type> {
|
||||
size: u64
|
||||
data: t*
|
||||
}
|
||||
|
||||
A specialization for strings:
|
||||
A specialization for strings:
|
||||
|
||||
const string_view = array_view<u8>
|
||||
const string_view = array_view<u8>
|
||||
|
||||
(String literals compile into string_view objects.)
|
||||
(String literals compile into string_view objects.)
|
||||
|
||||
Range interface for built-in arrays and for array_view.
|
||||
Range interface for built-in arrays and for array_view.
|
||||
|
||||
Numeric ranges with signatures
|
||||
Numeric ranges with signatures
|
||||
|
||||
range(end) // begin implicitly zero
|
||||
range(begin, end) // step implicitly one
|
||||
range(begin, end, step)
|
||||
|
||||
that allow iteration like
|
||||
range(end) // begin implicitly zero
|
||||
range(begin, end) // step implicitly one
|
||||
range(begin, end, step)
|
||||
|
||||
that allow iteration like
|
||||
|
||||
for i in range(10):
|
||||
for i in range(5u, 10u):
|
||||
for i in range(1.0, 10.0, 0.5):
|
||||
for i in range(10) { ... }
|
||||
for i in range(5u, 10u) { ... }
|
||||
for i in range(1.0, 10.0, 0.5) { ... }
|
||||
|
||||
======== MODULES AND IMPORTS ========
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue