#! /usr/bin/pan /"~/.config/sublime-text-3/Packages/User/Pan.sublime-syntax // https://www.sublimetext.com/docs/syntax.html // https://raw.githubusercontent.com/kkos/oniguruma/v6.9.1/doc/RE // https://github.com/kkos/oniguruma/ // https://www.sublimetext.com/docs/scope_naming.html // https://www.sublimetext.com/docs/color_schemes.html // https://www.sublimetext.com/docs/packages.html#locations // nil -vs- null // pub -vs- export import export // ? vs pub pub var mut if else switch case break for while continue fn return defer errdefer infer orelse error try catch comptime // :#[_]u8 = ...; // comptime array of u8 ? test assert // XXX dis // discard var x :u8 = 255; // max var _ :s8 = -128; // min var _ :u16 = 65535; // max var _ :s16 = -32768; // min var _ :u32 = 0xffff_ffff; // max var _ :s32 = -0x8000_0000; // min var _ :u64 = 0x_ffff_ffff_ffff_ffff; // max var _ :s64 = 0x_7fff_ffff_ffff_ffff; // max var _ :usize = @ptr( usize, null ); var _ :usize = @ptr( ssize, null ); // what did i mean with "usize = @ptr( usize, null )" ? // ..must have been a cast *to* a pointer *from* a usize ?? // maybe `var my_ptr :*u8 = @ptr( 0x_dead_beef );` ? // but then why not `var my_ptr = @as( *u8, 0x_dead_beef );` ..? // side-note: this is a way to bypass the type-system, // & give oneself null-pointer-exceptions, even in a null-safe lang.. var _ :f16 = 0.5; var _ :f32 = undefined; var _ :f64 = undefined; var _ :bool = true; var _ :void = undefined; // don't know what this would look like.. var _ :#type = u8; // # comptime var _ :*u8 = &x; // pointer, never null var mut y :u8 = 128; mut z :u8 = 255; // ... should `var` be required when `mut` is already ? var i :mut u8 = 129; // XXX not sure how i like this... i += 1; // increment, loop control var _ :*mut u8 = &y; // constant pointer to mutable u8 var _ :mut *u8 = &x; // mutable pointer to constant u8 var _ :?*usize = null; // optional, nullable pointer var _ :*usize = @as( *u8, 0xdead_beef ); // XXX for embedded var _ :[2]u8 = .( 0, 255 ); var _ :[3]u8 = [3]u8( 1, 2, 3 ); var _ :[4]u8 = [4]u8( 'g','o','o','d' ); var _ :[4]u8 = "good"; // same, shorter // var _ : []u8 = &"good"; // different, native slice // .( .ptr :[*]u8, .len :usize = 4 ) // var _ : []u8 = "good"; // different, native slice // .( .ptr :[*]u8, .len :usize = 4 ) // are all intermediate values considered const /non-mut unless explicitly made mut? // so `var str :[]u8 = &"again";` is fine, because the slice contents is const, // but `var mut array = "another"; var slc :[]mut u8 = &array;` // needs the array explicitly declared mutable?.. // seems consistent, at least. var _ :[5]u8 = "hello"; var _ :[11]u8 = "lorem ipsum"; var _ = "lorem ipsum"; // [11]u8 // [_]u8( 'l','o','r', ... ); var _ = "lorem ipsum"; // []u8 // var _ :*[2]u8 = &[_]u8( 0, 1 ); var _ : []u8 = &[_]u8( 0, 1 ); // .( .ptr :[*]u8 = ..., .len :usize = 2 ); // slice, native var _ : []u8 = &.( 0, 1 ); var _ :range = 0..10; // .{ .lo :ssize = 0; .hi :ssize = 10 }; // .lo <= _ < .hi // should .lo < .hi ? should .lo be negative? // NO, because zero-sized ranges are valid. // should .lo <= .hi ? ..? // definitely integer.. thinking negatives allowed, but assert( .lo <= .hi ); // except ranges are going to be primarily used for indexing into arrays/slices, // so usize is to be preferred // perhaps two ranges ?.. .( .lo :ssize, .hi :ssize, ); and .( .lo :usize, .hi :usize, ); // .. the most common case is: positive, { .lo <= .hi }, counting up. hello world // TODO decide between ( null & nil ) null // keyword nil // keyword undefined // keyword unreachable(); // stdlib assert( true ); // stdlib true false // pan / pace / piece / peace // .. pan, paan, bread "goodbye" "hello \" world" "\t\t\tmultiple escapes.. highlights all, requires push scope" "invalid unfinished quote /string /"roses are red /"violets are blue // sequential line strings are joined with newlines // blank lines & comments between line strings are weird, but still correct, i think. /"line string with a newline at the end /" "normal strings can end with newlines as well\n" var mut my_str = "string with a quote \" \n"; // this string has an unmatched quote var unfinished_string = "it's nice to meet y // line strings are not ideal for a single line // since the expression/ statement has to continue on the next l8ne then.. var line_string = /"this can contain quotes """ see ? ; { // `if` expressions var x :u8 = 17; var y :?u8 = if( math.rand( u8 ) < 128 ) { x }; var z :[]u8 = if( math.rand( s8 ) < 0 ) { "negative" } else { "positive" } ; } { // while stream & expression var stream = fn() ?u8 { return if( math.rand( bool ) ) { math.rand( u8 ) } else { null }; }; // TODO iterator example var one_or_large = while( stream() ) |byte| { stdout.print( "{d}\n", .( byte ) ); if( 128 < byte ) { break byte; } } orelse { 1 }; } { // linked list iterator var Linked_list = fn( T :type ) :type { // ??? does Node need to be mutable to add a fn after ? var mut Node = struct ( next :?*@self(); item :T; ); var Iterator :type = struct( .node :?*Node = null; ::next = fn( self :*mut @self() ) :?T { return if( self.*.node ) |node| { var item = node.*.item; self.*.node = node.*.next; item // defer { self.*.node = node.*.next; } // node.*.item } else { null }; }; ); // TODO syntax for type-functions... Node::iterator = fn( root :*@self() ) :Iterator { return .( .node = root ); }; // Node::iterator :fn( :*@self() ):Iterator = // .|self| { .( .node = self ) } // ; return Node; // Node // ??? should functions allow block expression returns ? }; // var node :Linked_list( u8 ) = .{ .next = null; .item = 1; }; // var root :Linked_list( u8 ) = .{ .next = &node; .item = 2; }; var root :Linked_list( u8 ) = .( .item = 1; .next = &.( .item = 2; .next = &.( .item = 3; .next = &.( .item = 4; .next = &.( .item = 5; .next = null; ); ); ); ); ); var mut iterator = root::iterator(); var mut sum :usize = 0; var four :?u8 = while( iterator::next() ) |val| { if( val == 4 ) { break val; } sum += val; }; assert { sum == 6 }; var still_four :u8 = four orelse 0; } { // function lambdas ?? var addition :fn( :u8, :u8 ):u8 = .| x, y | { x + y }; var subtraction :fn( :u8, :u8 ):u8 = .| x, y | { x - y }; var multiplication :fn( :u8, :u8 ):u8 = .| x, y | { x * y }; var division :fn( :u8, :u8 ):u8 = .| x, y | { x / y }; // div-by-zero ? var modulus :fn( :u8, :u8 ):u8 = .| x, y | { x % y }; // all functions are referred to via pointers // **function expressions evaluate to fn pointers** // var binary_functions = &[_]fn( :u8, :u8 ):u8 { addition, subtraction, multiplication, division, modulus }; var binary_functions = .{ addition, subtraction, multiplication, division, modulus }; var names = .{ "addition", "subtraction", "multiplication", "division", "modulus" }; for( binary_functions, names ) |fx, name| { var x = 23; var y = 7 var z = fx( 23, 7 ); print( "{s}: fx( {d}, {d} ) = {d};\n", .( name, x, y, z ) ); // addition: fx( 23, 7 ) = 30; // subtraction: fx( 23, 7 ) = 16; // multiplication: fx( 23, 7 ) = 161; // division: fx( 23, 7 ) = 3; // modulus: fx( 23, 7 ) = 2; } } var fin = { var mut i = 0; while( i < 10 ) { log( i ); i += 1; }; for( 0..10 ) |j| { log( j ); }; // .. how to label loops ? // `loop asdf while/for(...)` ? // okay to be verbose in uncommon cases // are loops the only location for labels ? // .. `label my_label while/for(...)` ? "fin" }; struct tuple union enum slice // []u8 // .( .ptr :[*]u8; .len :usize; ); range // lo..hi // .( .lo :usize; .hi :usize; ); // within `.( ... );` comma `,` should probably be preferred over semicolon `;` ... // switch compilation - https://en.wikipedia.org/wiki/Switch_statement#Compilation // duff's device - https://en.wikipedia.org/wiki/Duff%27s_device // branch table - https://en.wikipedia.org/wiki/Branch_table // otherwise, binary search... // TODO consider range lo/hi fields & values... positive/negative & lo> 6 ]; var chunk_mask :u64 = 1 << { 0b_0011_1111 & char }; var flag = chunk & chunk_mask; return flag != 0; }; /// for parsing ie. "[^a-z234\n\xFF]". /// on success, consumes the closing bracket ']'. /// XXX atoms toggle, double negation /// `[a-cd-f]` == `[abcdef]` /// XXX `[a-dc-f]` == `[abef]`, should be `[abcdef]` Char_Set::parse = fn( src :*mut []u8 ) :!Char_Set { consume( src, '[' ).!; // TODO consider reseting src on error // var original_src = src; // errdefer { src = original_src; }; var mut char_set :Char_Set = .{}; var is_inverted = { 0 < src.len and src[0] == '^' }; var fill :u64 = if( is_inverted ) { 0x_ffff_ffff_ffff_ffff } else { 0 }; for( char_set.bits ) |*chunk| { chunk.* = fill; }; var begin :usize = if( is_inverted ) { 1 } else { 0 }; src.* = src[begin..]; while( 0 < src.len ) { var atom = parse_atom( src, ']' ).! orelse { src.* = src[1..]; // consume( src, ']' ).!; return char_set; }; if( 1 < src.len and src[0] == '-' ) { src.* = src[1..]; // consume( src, '-' ).!; var atom_range_end = parse_atom( src, ']' ).! orelse { src.* = src[1..]; // consume( src, ']' ).!; char_set::toggle_char( atom ); char_set::toggle_char( '-' ); return char_set; }; char_set::toggle_range( atom, atom_range_end ); } else { char_set::toggle_char( atom ); }; }; return error.end_of_input; //return while( 0 < src.len ) { // var atom = Char_Set::parse_atom( src ).! // orelse { break char_set; } // ; // if( 1 < src.len and src[0] == '-' ) { // src.* = src[1..]; // var atom_range_end = Char_Set::parse_atom( src ).! orelse { // char_set::toggle_char( atom ); // char_set::toggle_char( '-' ); // break char_set; // }; // char_set::toggle_range( atom, atom_range_end ); // } else { // char_set::toggle_char( atom ); // }; //} orelse { error.end_of_input }; }; //Char_Set::parse_atom = fn( content :*mut []u8 ) !u8 { // // XXX using ptr_to_slice.*[index] syntax here feels bad.. // // parse atoms: `a`, `\n`, `\xff` // if( 1 <= content.*.len and content.*[0] != '\\' ) { // // content.*[0..1]; // defer { content.* = content.*[1..]; }; // return content.*[0]; // }; // if( 2 <= content.*.len and content.*[1] != 'x' ) { // // content.*[0..2]; // defer { content.* = content.*[2..]; }; // // https://ziglang.org/documentation/master/#Escape-Sequences // // https://en.wikipedia.org/wiki/ASCII#Control_code_chart // return switch( content.*[1] ) { // .( '0' ) { 0x00 }, // null // .( 't' ) { 0x09 }, // tab // .( 'n' ) { 0x0A }, // line feed // .( 'v' ) { 0x0B }, // vertical tab // .( 'f' ) { 0x0C }, // form feed // .( 'r' ) { 0x0D }, // carriage return // .() { content.*[1] }, // }; // }; // if( 4 <= content.*.len ) { // // content.*[0..4]; // defer { content.* = content.*[4..]; }; // return hex_to_byte( content.*[2], content.*[3] ).!; // }; // return error.partial_atom; //}; /// for parsing char_set atoms like `a`, `\n`, `\xff`. /// modifies `content`, consumes atom. /// - errors on end-of-input or invalid-hex. /// - null on ']', end-of-char_set. /// * ']' can be returned as u8 via "\]" or "\x5d". /// * '\' can be returned as u8 via "\\" or "\x__". /// - u8 on atom, normal. // Char_Set::parse_atom = fn( content :*mut []u8 ) :!?u8 { // return parse_atom( content, ']' ).!; // }; Char_Set::toggle_char = fn( self :*mut Char_Set, char :u8 ) :void { var chunk_mask :u64 = 1 << { 0b_0011_1111 & char }; self.*.bits[ char >> 6 ] ^= chunk_mask; }; Char_Set::toggle_range = fn( self :*mut Char_Set, begin :u8, end :u8 ) :void { // XXX terribly inefficient, but functional // also, what if { end < begin } ..? for( begin..end ) |i| { self.*::toggle_char( i ); }; self.*::toggle_char( end ); // inclusive }; Char_Set }; /// for parsing char_set atoms like `a`, `\n`, `\xff`. /// modifies `src`, consumes atom. /// - errors on end-of-input or invalid-hex. /// - null on terminal ( end of char_set ']' or literal '"' ) /// * '"' can be returned as u8 via "\"" or "\x22". /// * ']' can be returned as u8 via "\]" or "\x5d". /// * '\' can be returned as u8 via "\\" or "\x5c". /// - u8 on atom, normal. var parse_atom = fn( src :*mut []u8, terminal :u8 ) :!?u8 { // parse atoms: `a`, `\n`, `\xff` var mut atom_len = 0; defer { src.* = src[atom_len..]; }; if( src.len < 1 ) { return error.partial_atom_0; }; atom_len = 1; var a0 = src[0]; if( a0 == terminal ) { return null; }; if( a0 != '\\' ) { return a0; }; // `a` // a0 is `\` if( src.len < 2 ) { return error.partial_atom_1; }; atom_len = 2; var a1 = src[1]; if( a1 != 'x' ) { // https://ziglang.org/documentation/master/#Escape-Sequences // https://en.wikipedia.org/wiki/ASCII#Control_code_chart return switch( a1 ) { // `\q` .( '0' ) { 0x00 }, // null .( 't' ) { 0x09 }, // tab .( 'n' ) { 0x0A }, // line feed .( 'v' ) { 0x0B }, // vertical tab .( 'f' ) { 0x0C }, // form feed .( 'r' ) { 0x0D }, // carriage return .() { a1 }, }; }; // a0 is `\`, a1 is `x`, src[0..2] is `\x` if( src.len < 4 ) { return error.partial_atom_4; }; atom_len = 4; var a3 = src[2]; var a4 = src[3]; return hex_to_byte( a3, a4 ).!; // `\xff` }; var hex_to_nibble = fn( hex :u8 ) :!u8 { return if( '0' <= hex and hex <= '9' ) { hex - '0' } else if( 'a' <= hex and hex <= 'f' ) { hex - 'a' } else if( 'A' <= hex and hex <= 'F' ) { hex - 'A' } else { error.invalid_hex }; }; var hex_to_byte = fn( hi :u8, lo :u8 ) :!u8 { return ( hex_to_nibble( hi ).! << 4 ) | hex_to_nibble( lo ).!; }; var consume = fn( src :*mut[]u8, char :u8 ) :!void { if( 0 < src.len ) { if( src[0] == char ) { src.* = src[1..]; } else { return error.bad_char; }; } else { return error.early_eof; }; };