Switch to curly braces for scoping instead of indentation

This commit is contained in:
Nikita Lisitsa 2026-07-27 16:12:56 +03:00
parent 9337490f5b
commit 893aa5d979
21 changed files with 692 additions and 728 deletions

View file

@ -187,7 +187,7 @@ int main(int argc, char ** argv)
if (dump_ast) if (dump_ast)
{ {
std::cout << "Input file " << filenames.back() << " AST dump:\n\n"; 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); ast::print(std::cout, *function_definition->statements);
std::cout << "\n" << std::flush; std::cout << "\n" << std::flush;
} }

View file

@ -4,81 +4,92 @@ import components
import ecs import ecs
const x = 10s // deduced type i16 const x = 10s // deduced type i16
var y = 14u // deduced type u32 mut y = 14u // deduced type u32
var z: f64 = 3.14l mut z: f64 = 3.14l
func fma(x: f32, y: f32, z: f32) -> f32: func fma(x: f32, y: f32, z: f32) -> f32 {
return x * y + z return x * y + z
}
struct vec2: struct vec2 {
x: f32 x: f32
y: f32 y: f32
}
// pass by value // pass by value
func length(v: vec2) -> f32: func length(v: vec2) -> f32 {
return math.sqrt(v.x * v.x + v.y * v.y) return math.sqrt(v.x * v.x + v.y * v.y)
}
// return type deduced as u64 func merge(x: u32, y: u32) -> u64 {
func merge(x: u32, y: u32):
return (x as u64) or ((y as u64) << 32) return (x as u64) or ((y as u64) << 32)
}
var v = vec2(10, 20) mut v = vec2(10, 20)
length(v) length(v)
// can be called using method syntax // can be called using method syntax
v.length() v.length()
// function pointers // 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 * // pass by reference/pointer with *
// TODO: const pointer? // TODO: const pointer?
func my_system(event: events.update, position: *components.position, velocity: *components.velocity): func my_system(event: events.update, position: components.position mut*, velocity: components.velocity*) {
position += event.dt * velocity position += event.dt * velocity
}
func attach(dispatcher: *ecs.dispatcher): func attach(dispatcher: ecs.dispatcher*) {
// TODO: how does it work? C++-style variadic templates? Oh no... // TODO: how does it work? C++-style variadic templates? Oh no...
dispatcher.system(my_system) dispatcher.system(my_system)
}
// objects with methods // objects with methods
struct rectangle: struct rectangle {
width: i32 width: i32
height: i32 height: i32
}
func extend(r: &rectangle, size: i32): func extend(r: rectangle mut*, size: i32) {
r.width += size r.width += size
r.height += size r.height += size
}
var r = rectangle(10, 12) mut r = rectangle(10, 12)
r.extend(5) r.extend(5)
// named initializers // named initializers
var r2 = rectangle(width = 20, height = 30) mut r2 = rectangle(width = 20, height = 30)
// regular pointers // regular pointers
var ptr: *i32 = null mut ptr: i32* = null
var x = 15 mut x = 15
ptr = &x ptr = &x
// field/method access using pointers is the same as with values // field/method access using pointers is the same as with values
var sptr: *rectangle = &r mut sptr: rectangle mut* = &r
r.width *= 2 r.width *= 2
// simple generics // simple generics
struct array(T): struct array(T) {
data: *T data: *T
size: u64 size: u64
}
// TODO: constructors? destructors? // TODO: constructors? destructors?
func new(self: array(T), size: u64): func new(self: array(T), size: u64) {
return array(T)(data = mem.alloc(size * sizeof(T)), size = size) return array(T)(data = mem.alloc(size * sizeof(T)), size = size)
}
// TODO: static arrays? // TODO: static arrays?
// TODO: move-only types? alloc returns smth like unique ptr? // TODO: move-only types? alloc returns smth like unique ptr?
struct kvpair(K, V): struct kvpair(K, V) {
key: K key: K
value: V value: V
}
struct arraymap(K, V): struct arraymap(K, V) {
values: array(kvpair(K, V)) values: array(kvpair(K, V))
}

View file

@ -1,7 +1,8 @@
foreign func sin(x: f64) -> f64 foreign func sin(x: f64) -> f64
foreign func cos(x: f64) -> f64 foreign func cos(x: f64) -> f64
func test(x: f64) -> f64: func test(x: f64) -> f64 {
let s = sin(x) let s = sin(x)
let c = cos(x) let c = cos(x)
return s * s + c * c return s * s + c * c
}

View file

@ -1,40 +1,48 @@
func print(c: u8): func print(c: u8) {
foreign func putchar(c: i32) -> i32 foreign func putchar(c: i32) -> i32
putchar(c as i32) putchar(c as i32)
}
func print_i32(x: i32): func print_i32(x: i32) {
if x < 0: if x < 0 {
print('-') print('-')
print_i32(-x) print_i32(-x)
return return
if x >= 10: }
if x >= 10 {
print_i32(x / 10) print_i32(x / 10)
}
print('0' + (x % 10 as u8)) print('0' + (x % 10 as u8))
}
func print_f32(x: f32): func print_f32(x: f32) {
if x < 0.0: if x < 0.0 {
print('-') print('-')
print_f32(-x) print_f32(-x)
return return
}
foreign func floorf(x: f32) -> f32 foreign func floorf(x: f32) -> f32
let floor = floorf(x) as i32 let floor = floorf(x) as i32
print_i32(floor) print_i32(floor)
print('.') print('.')
mut y = x - (floor as f32) mut y = x - (floor as f32)
mut i = 0 mut i = 0
while i < 5: while i < 5 {
y = y * 10.0 y = y * 10.0
let yfloor = floorf(y) as i32 let yfloor = floorf(y) as i32
print('0' + (yfloor as u8)) print('0' + (yfloor as u8))
y = y - (yfloor as f32) y = y - (yfloor as f32)
i = i + 1 i = i + 1
}
}
struct vec3: struct vec3 {
x: f32 x: f32
y: f32 y: f32
z: f32 z: f32
}
func print_vec3(v: vec3): func print_vec3(v: vec3) {
print('(') print('(')
print_f32(v.x) print_f32(v.x)
print(',') print(',')
@ -42,31 +50,39 @@ func print_vec3(v: vec3):
print(',') print(',')
print_f32(v.z) print_f32(v.z)
print(')') 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 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) 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) 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 foreign func sqrtf(x: f32) -> f32
return mult(v, 1.0 / sqrtf(dot(v, v))) return mult(v, 1.0 / sqrtf(dot(v, v)))
}
struct ray: struct ray {
origin: vec3 origin: vec3
direction: 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) return (value - dot(ray.origin, normal)) / dot(ray.direction, normal)
}
func test() -> i32[3]: func test() -> i32[3] {
return [70, 60, 50] return [70, 60, 50]
}
func print_i32_3(a: i32[3]): func print_i32_3(a: i32[3]) {
print('[') print('[')
print_i32(a[0]) print_i32(a[0])
print(',') print(',')
@ -74,6 +90,7 @@ func print_i32_3(a: i32[3]):
print(',') print(',')
print_i32(a[2]) print_i32(a[2])
print(']') print(']')
}
mut a = [1, 2, 3] mut a = [1, 2, 3]
a = test() a = test()

View file

@ -1,11 +1,13 @@
func print(c: u8): func print(c: u8) {
foreign func putchar(c: i32) -> i32 foreign func putchar(c: i32) -> i32
putchar(c as i32) putchar(c as i32)
}
func test() -> i32: func test() -> i32 {
global mut x = 0 global mut x = 0
x += 1 x += 1
return x return x
}
print('0' + (test() as u8)) print('0' + (test() as u8))
print('0' + (test() as u8)) print('0' + (test() as u8))

View file

@ -1,32 +1,37 @@
func print(c: u8) {
foreign func putchar(c: i32) -> i32 foreign func putchar(c: i32) -> i32
func print(c: u8):
putchar(c as i32) putchar(c as i32)
}
func mandelbrot(): func mandelbrot() {
let width = 120 let width = 120
let height = 40 let height = 40
mut y = 0 mut y = 0
while y < height: while y < height {
mut x = 0 mut x = 0
while x < width: while x < width {
let cx = (x as f32 + 0.5) / (width as f32) * 2.5 - 2.0 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 let cy = (y as f32 + 0.5) / (height as f32) * 2.0 - 1.0
mut tx = 0.0 mut tx = 0.0
mut ty = 0.0 mut ty = 0.0
mut i = 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 let newx = tx * tx - ty * ty + cx
ty = 2.0 * tx * ty + cy ty = 2.0 * tx * ty + cy
tx = newx tx = newx
i = i + 1 i = i + 1
if i == 100: }
if i == 100 {
print('X') print('X')
else: } else {
print(' ') print(' ')
}
x = x + 1 x = x + 1
}
y = y + 1 y = y + 1
print('\n') print('\n')
}
}
mandelbrot() mandelbrot()

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,15 @@
func g() -> u16: func g() -> u16 {
return g() return g()
}
func h() -> bool: func h() -> bool {
return h() return h()
}
func test_and() -> u16: func test_and() -> u16 {
return 0us && g() return 0us && g()
}
func test_or() -> u16: func test_or() -> u16 {
return 65535us || g() return 65535us || g()
}

View file

@ -1,10 +1,13 @@
struct vec2f: struct vec2f {
x: f32 x: f32
y: f32 y: f32
}
struct body: struct body {
position: vec2f position: vec2f
rotation: f32 rotation: f32
}
func move_x(b: body mut*, delta: f32): func move_x(b: body mut*, delta: f32) {
(*b).position.x = (*b).position.x + delta b.position.x = b.position.x + delta
}

View file

@ -1,11 +1,13 @@
// Vectors // Vectors
struct vec2: struct vec2 {
x : f32 x : f32
y : 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) return vec2(a.x + b.x, a.y + b.y)
}
mut v = add(vec2(1.0, 2.0), vec2(3.0, 4.0)) mut v = add(vec2(1.0, 2.0), vec2(3.0, 4.0))
v.x = -v.x v.x = -v.x
@ -13,34 +15,39 @@ v.y = -v.y
// Factorial // Factorial
func factorial(n : u32) -> u32: func factorial(n : u32) -> u32 {
if n == 0u: if n == 0u { return 1u }
return 1u
return n * factorial(n - 1u) return n * factorial(n - 1u)
}
let factorial10 = factorial(10u) let factorial10 = factorial(10u)
// Fibonacci // Fibonacci
func fib(n : u32) -> u32: func fib(n : u32) -> u32 {
// Slow implementation with // Slow implementation with
// exponentially-growing recursion tree // exponentially-growing recursion tree
if n == 0u | n == 1u: if n == 0u | n == 1u {
return n // base case // Base case
return n
}
return fib(n - 1u) + fib(n - 2u) return fib(n - 1u) + fib(n - 2u)
}
let fib10 = fib(10u) let fib10 = fib(10u)
func h() -> u32: func h() -> u32 {
return 0u return 0u
}
func f() -> u32: func f() -> u32 {
return h() return h()
}
func g() -> u32: func g() -> u32 {
func h() -> u32: func h() -> u32 { return 1u }
return 1u
return f() return f()
}
// Should equal 0u, but equals 1u due to an error in // Should equal 0u, but equals 1u due to an error in
// how the interpreter resolves functions & variables // how the interpreter resolves functions & variables

View file

@ -20,29 +20,6 @@ namespace pslang::ast
ast::location location; 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< using statement_impl = std::variant<
expression_ptr, expression_ptr,
assignment, assignment,
@ -63,7 +40,6 @@ namespace pslang::ast
using statement_impl::statement_impl; using statement_impl::statement_impl;
}; };
location get_location(pre_statement const & statement);
location get_location(statement const & statement); location get_location(statement const & statement);
} }

View file

@ -6,23 +6,15 @@
namespace pslang::ast namespace pslang::ast
{ {
struct pre_statement;
struct statement; struct statement;
using pre_statement_ptr = std::shared_ptr<pre_statement>;
using statement_ptr = std::shared_ptr<statement>; using statement_ptr = std::shared_ptr<statement>;
struct pre_statement_list
{
std::vector<pre_statement_ptr> statements;
};
struct statement_list struct statement_list
{ {
std::vector<statement_ptr> statements; 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>; using statement_list_ptr = std::shared_ptr<statement_list>;
} }

View file

@ -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) location get_location(statement const & statement)
{ {
return std::visit(get_location_visitor{}, statement); return std::visit(get_location_visitor{}, statement);

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <pslang/parser/indented_statement.hpp> #include <pslang/ast/statement_fwd.hpp>
namespace pslang::ast namespace pslang::ast
{ {
@ -15,7 +15,7 @@ namespace pslang::parser
struct context struct context
{ {
ast::location & location; ast::location & location;
indented_statement_list & result; ast::statement_list_ptr & result;
}; };
} }

View 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);
}

View file

@ -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);
}

View file

@ -19,7 +19,7 @@ using bp = ::pslang::parser::bison::parser;
ctx.location.step(); ctx.location.step();
%} %}
[ ]+ { ctx.location.step(); } [ \r\t]+ { ctx.location.step(); }
"//"[^\n]* { return bp::make_comment(ctx.location); } "//"[^\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); } [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); } "\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_assignment(ctx.location); }
":" { return bp::make_colon(ctx.location); } ":" { return bp::make_colon(ctx.location); }
"," { return bp::make_comma(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_rparen(ctx.location); }
"[" { return bp::make_lbracket(ctx.location); } "[" { return bp::make_lbracket(ctx.location); }
"]" { return bp::make_rbracket(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_plus_assignment(ctx.location); }
"-=" { return bp::make_minus_assignment(ctx.location); } "-=" { return bp::make_minus_assignment(ctx.location); }
"*=" { return bp::make_asterisk_assignment(ctx.location); } "*=" { return bp::make_asterisk_assignment(ctx.location); }

View file

@ -20,7 +20,6 @@
%code requires { %code requires {
#include <pslang/parser/indented_statement.hpp>
#include <pslang/ast/statement.hpp> #include <pslang/ast/statement.hpp>
namespace pslang::parser { namespace pslang::parser {
@ -75,8 +74,7 @@ template <typename T>
%define api.token.prefix {tok_} %define api.token.prefix {tok_}
%token newline "newline" %token newline "newline"
%token indent "indentation" %token comment "comment"
%token comment
%token assignment "=" %token assignment "="
%token colon ":" %token colon ":"
%token comma "," %token comma ","
@ -85,6 +83,8 @@ template <typename T>
%token rparen ")" %token rparen ")"
%token lbracket "[" %token lbracket "["
%token rbracket "]" %token rbracket "]"
%token lbrace "{"
%token rbrace "}"
%token plus "+" %token plus "+"
%token minus "-" %token minus "-"
%token asterisk "*" %token asterisk "*"
@ -184,10 +184,12 @@ template <typename T>
%precedence else %precedence else
%precedence lbracket %precedence lbracket
%type <indented_statement_list> indented_statement_list %type <ast::statement_list> statement_list
%type <indented_statement> statement_line %type <ast::statement_list_ptr> statement_block
%type <std::size_t> indentation %type <ast::statement> statement_line
%type <ast::pre_statement> statement %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>> function_declaration_argument_list
%type <std::vector<ast::function_declaration::argument>> nonempty_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 %type <ast::function_declaration::argument> function_declaration_single_argument
@ -199,6 +201,8 @@ template <typename T>
%type <types::primitive_type> primitive_type %type <types::primitive_type> primitive_type
%type <std::vector<ast::type_ptr>> function_paren_type_list %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::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> expression
%type <ast::expression> postfix_expression %type <ast::expression> postfix_expression
%type <ast::expression> base_expression %type <ast::expression> base_expression
@ -209,27 +213,26 @@ template <typename T>
%% %%
module module
: indented_statement_list end { ctx.result = $1; } : statement_list end { ctx.result = std::make_unique<ast::statement_list>($1); }
; ;
indented_statement_list statement_list
: statement_line { indented_statement_list tmp; tmp.statements.push_back(std::move($1)); $$ = std::move(tmp); } : statement_line { ast::statement_list tmp; tmp.statements.push_back(std::make_unique<ast::statement>($1)); $$ = std::move(tmp); }
| empty_line { $$ = {}; } | empty_line { $$ = {}; }
| indented_statement_list newline statement_line { auto tmp = $1; tmp.statements.push_back(std::move($3)); $$ = std::move(tmp); } | statement_list newline statement_line { auto tmp = $1; tmp.statements.push_back(std::make_unique<ast::statement>($3)); $$ = std::move(tmp); }
| indented_statement_list newline empty_line { $$ = $1; } | statement_list newline empty_line { $$ = $1; }
;
statement_block
: lbrace statement_list rbrace { $$ = std::make_unique<ast::statement_list>($2); }
; ;
statement_line statement_line
: indentation statement optional_comment { $$ = indented_statement{$1, std::make_unique<ast::pre_statement>($2)}; } : statement optional_comment { $$ = $1; }
; ;
empty_line empty_line
: indentation optional_comment : 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 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), @$ }), @$ }; } | 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; } | variable_declaration { $$ = $1; }
| if expression colon { $$ = ast::if_block{std::make_unique<ast::expression>($2), @$}; } | if_chain { $$ = $1; }
| else colon { $$ = ast::else_block{@$}; } | while expression statement_block { $$ = ast::while_block{std::make_unique<ast::expression>($2), $3, @$, @$}; }
| 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), {}, @$, @$}; }
| break { $$ = ast::break_statement{@$}; } | break { $$ = ast::break_statement{@$}; }
| continue { $$ = ast::continue_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, @$}}; } | 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 expression { $$ = ast::return_statement{std::make_unique<ast::expression>($2), @$}; }
| return { $$ = ast::return_statement{nullptr, @$}; } | return { $$ = ast::return_statement{nullptr, @$}; }
| struct name colon { $$ = ast::struct_definition{$2, {}, @$, @$}; } | struct name lbrace field_definition_list rbrace { $$ = ast::struct_definition{$2, $4, merge(@1, @2), @$}; }
| name colon type_expression { $$ = ast::field_definition{$1, std::make_unique<ast::type>($3), @$}; } ;
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 function_declaration_argument_list
@ -301,6 +311,17 @@ variable_keyword
| mut { $$ = ast::value_category::_mutable; } | 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 type_expression
: unit { $$ = types::unit_type{}; } : unit { $$ = types::unit_type{}; }
| primitive_type { $$ = ast::type($1); } | primitive_type { $$ = ast::type($1); }

View file

@ -1,4 +1,4 @@
#include <pslang/parser/indented_statement.hpp> #include <pslang/parser/finalize.hpp>
#include <pslang/parser/error.hpp> #include <pslang/parser/error.hpp>
#include <pslang/ast/statement.hpp> #include <pslang/ast/statement.hpp>
#include <pslang/ast/statement_visitor.hpp> #include <pslang/ast/statement_visitor.hpp>
@ -11,265 +11,50 @@ namespace pslang::parser
namespace namespace
{ {
struct fill_location_visitor void validate(ast::statement_list_ptr statements, ast::function_definition * in_function, bool in_loop)
: ast::statement_visitor<fill_location_visitor>
{ {
using statement_visitor::apply; for (auto & statement : statements->statements)
ast::location apply(ast::expression_ptr const & node)
{ {
return ast::get_location(*node); if (auto if_chain = std::get_if<ast::if_chain>(statement.get()))
{
for (auto const & block : if_chain->blocks)
{
validate(block.statements, in_function, in_loop);
} }
ast::location apply(ast::assignment const & node)
{
return node.location;
} }
else if (auto while_block = std::get_if<ast::while_block>(statement.get()))
ast::location apply(ast::variable_declaration const & node)
{ {
return node.location; validate(while_block->statements, in_function, true);
} }
else if (auto function_definition = std::get_if<ast::function_definition>(statement.get()))
ast::location apply(ast::if_chain & node)
{ {
bool first = true; validate(function_definition->statements, function_definition, in_loop);
for (auto & block : node.blocks)
{
block.location = apply(*block.statements);
if (first)
node.location = block.location;
else
node.location = ast::merge(node.location, block.location);
first = false;
} }
return node.location; else if (auto return_statement = std::get_if<ast::return_statement>(statement.get()))
}
ast::location apply(ast::while_block & node)
{ {
return node.location = ast::merge(node.prelude_location, apply(*node.statements)); if (!in_function)
}
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)
{
auto statement_location = apply(*statement);
if (first)
result = statement_location;
else
result = ast::merge(result, statement_location);
first = false;
}
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); throw parse_error("Return statement outside of function scope", return_statement->location);
return_statement->node = function_stack.back(); return_statement->node = in_function;
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())) else if (auto break_statement = std::get_if<ast::break_statement>(statement.get()))
{ {
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(*expression_ptr))); if (!in_loop)
}
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); 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())) else if (auto continue_statement = std::get_if<ast::continue_statement>(statement.get()))
{ {
if (loop_stack.empty()) if (!in_loop)
throw parse_error("Continue without an enclosing loop", continue_statement->location); 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 }
}
}
ast::statement_list_ptr finalize(ast::statement_list_ptr statements)
{ {
throw ast::invalid_ast_error(std::format("Unknown pre-statement \"{}\"", std::visit([](auto const & statement){ return typeid(statement).name(); }, *statement.statement)), location); validate(statements, nullptr, false);
} return statements;
if (list)
{
stack.push_back(list);
++current_indent;
}
}
fill_location_visitor{}.apply(*result);
return result;
} }
} }

View file

@ -1,6 +1,6 @@
#include <pslang/parser/parser.hpp> #include <pslang/parser/parser.hpp>
#include <pslang/parser/context.hpp> #include <pslang/parser/context.hpp>
#include <pslang/parser/indented_statement.hpp> #include <pslang/parser/finalize.hpp>
#include <pslang/ast/location.hpp> #include <pslang/ast/location.hpp>
#include "gen_parser.hpp" #include "gen_parser.hpp"
#include "gen_lexer.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))); throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)));
ast::location location{.begin = {.filename = path}, .end = {.filename = path}}; ast::location location{.begin = {.filename = path}, .end = {.filename = path}};
indented_statement_list statements; ast::statement_list_ptr statements;
context ctx{location, statements}; context ctx{location, statements};
bison::parser parser(ctx); bison::parser parser(ctx);

View file

@ -151,20 +151,24 @@ Special built-ins:
======== FLOW CONTROL ======== ======== FLOW CONTROL ========
Conditionals: Conditionals:
if condition: if condition {
statements statements
else if condition: } else if condition {
statements statements
else: } else {
statements statements
}
While loop: While loop:
while condition: while condition {
statements statements
if x: if x {
break break
if y: }
if y {
continue continue
}
}
Iterator interface: Iterator interface:
get(it) returns the currently pointed-to value get(it) returns the currently pointed-to value
@ -178,33 +182,37 @@ Range interface:
For loop: For loop:
Operates only on ranges. Operates only on ranges.
for x in range(10): for x in range(10) {
do_something(x) 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: for &x in array {
*x += 1 *x += 1
}
The loop is equivalent to The loop is equivalent to
mut begin = begin(range) mut it = begin(range)
let end = end(range) let end = end(range)
while begin != end: while it != end {
let x = get(begin) // or get_ref(x) for pointer loop let x = get(it) // or get_ref(x) for pointer loop
statements statements
begin = next(begin) 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 ======== ======== STRUCTS ========
Struct types: Struct types:
struct rect: struct rect {
width: u32 width: u32
height: u32 height: u32
}
Creating a struct value: Creating a struct value:
let x = rect(10u, 20u) let x = rect(10u, 20u)
@ -221,20 +229,26 @@ Struct field access:
======== FUNCTIONS ======== ======== FUNCTIONS ========
Function definition: Function definition:
func foo(x: i32, y: i32) -> i32: func foo(x: i32, y: i32) -> i32 {
return x * y return x * y
}
func bar(x: f32): // deduced return type unit func bar(x: f32) { // deduced return type unit
print(x) 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:
func bar(mut x: u32) -> u32 {
x = (x + 1u)
x = x * x
return x
}
// External function: name taken literally as `powf` // External function: name taken literally as `powf`
// and C calling convention assumed // and C calling convention assumed
foreign func powf(x: f32, y: f32) -> f32 // no implementation foreign func powf(x: f32, y: f32) -> f32 // no implementation
// TODO: mutable function arguments?
// TODO: function overloading? Probably requires selecting a specific overload using `as` operator to save to a value (but not on call site) // 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? // 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. 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). `type` can be used in any place where a type is required (variable types, function arguments, function return value, etc).
E.g. E.g.
func foo(x: type) -> type: func foo(x: type) -> type {
return x[4] // type of arrays of 4 elements of type x return x[4] // type of arrays of 4 elements of type x
}
let y: type = u32 let y: type = u32
if foo(y) == u32[4]: if foo(y) == u32[4] {
do_smth() do_smth()
}
======== CONST EXPRESSIONS ======== ======== CONST EXPRESSIONS ========
@ -283,12 +299,14 @@ E.g.
// Functions returning functions/structs // Functions returning functions/structs
// Syntactic sugar for common cases // Syntactic sugar for common cases
// Figure out: max(a,b) - how to deduce type parameters? How does it play with overloading? // Figure out: max(a,b) - how to deduce type parameters? How does it play with overloading?
// func max(t: type): // func max(t: type) {
// return func(x : t, y : t): // return func(x : t, y : t) {
// if x > y: // if x > y:
// return x // return x
// else: // else:
// return y // return y
// }
// }
======== PRELUDE ======== ======== PRELUDE ========
@ -298,9 +316,10 @@ It contains:
An array_view template struct: An array_view template struct:
struct array_view<t: type>: struct array_view<t: type> {
size: u64 size: u64
data: t* data: t*
}
A specialization for strings: A specialization for strings:
@ -318,9 +337,9 @@ It contains:
that allow iteration like that allow iteration like
for i in range(10): for i in range(10) { ... }
for i in range(5u, 10u): for i in range(5u, 10u) { ... }
for i in range(1.0, 10.0, 0.5): for i in range(1.0, 10.0, 0.5) { ... }
======== MODULES AND IMPORTS ======== ======== MODULES AND IMPORTS ========