[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Documentation] Learn Poke in Y Minutes
From: |
Mohammad-Reza Nabipoor |
Subject: |
[Documentation] Learn Poke in Y Minutes |
Date: |
Sun, 28 Feb 2021 00:49:50 +0330 |
Hi, Jose.
After months, my tutorial is ready (from my PoV). I'd be glad if you review
it and if you like it, publish it on GNU poke web site.
I think it covers all the basics of Poke.
The emphasis of this document is not on "GNU poke", it's on the "Poke".
I suggest you to use `htmlfontify-buffer` command of Emacs to generate the
HTML output (See the attachment).
BTW there's a `FIXME`, I need your opinion there.
Regards,
Mohammad-Reza
---
/* Copyright (C) 2020-2021, Mohammad-Reza Nabipoor */
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* GNU poke is an interactive editor for binary data. But it's not just an
* editor, it provides a full-fledged procedural, interactive programming
* language designed to describe data structures and to operate on them.
* The programming language is called Poke (with upper-case P).
*
* When the user have a description of binary data (a *pickle*), he/she
* can *map* it on the actual data and start poking the data! The user can
* inspect and modify data.
*/
/* First start with nomenclature:
*
* - poke The editor program (also called GNU poke)
* - Poke Domain-specific programming language that used by `poke`
* - pickle A logical component that provides a set of (related)
* functionalities (e.g., description of a binary format or
* utilities to deal with date and time, etc.).
* Pickles are defined in files with `.pk` extension.
*/
/* Let's talk about the Poke! */
/* Variables
*
* We can define variables in Poke using `var` keyword:
*
* var NAME_OF_VARIABLE = VALUE
*/
var an_integer = 10;
var a_string = "hello, poke users!";
/* Values
*
* Poke programming language has the following types of value:
*
* - Integer
* - String
* - Offset
* - Array
* - Struct
* - Union
* - Function (or closure)
*
* There are two categories of values in Poke:
* - Simple values
* - Integer
* - String
* - Offset
* - Composite values
* - Array
* - Struct
* - Union
* - Function // FIXME not sure
*
* The difference lies in the semantics of copy. Consider the following `C`
* prgoram:
*
* ```c
* void f(int);
* void g(int*);
*
* int main() {
* int i = 10;
*
* f(i); // sends a copy of **value of** `i` to `f`
* g(&i); // sends a copy of **address of** `i` to `g`
*
* return 0;
* }
* ```
*
* Simple values in Poke are like `int` in `C`. The copy makes a new disjoint
* value. Changing the copied value will not change the original one.
* And composite values are like `int*` in `C`. The new copy will also points
* to the same data.
*/
/* Integer values */
var decimal = 10;
var hexadecimal = 0xff;
var binary = 0b1100;
var octal = 0o777;
var si8 = 1B; /* byte (8-bit) */
var si16 = 2H; /* byte (16-bit) */
var si32 = 3; /* int (32-bit) */
var si64 = 4L; /* long (64-bit) */
var ui8 = 4UB; /* unsigned byte (8-bit) */
var ui16 = 5UH; /* unsigned int (16-bit) */
var ui32 = 6U; /* unsigned int (32-bit) */
var ui64 = 7UL; /* unsigned long (64-bit) */
var long_decimal = 100_000_000;
var long_hexadecimal = 0x1122_aabb_ccdd_eeff;
/* Operations on integer values */
/* Arithmetic */
var ia1 = 10 ** 2; /* exponentiation -- ia1 == 100 */
var ia2 = 5 * 7; /* multiplication -- ia2 == 35 */
var ia3 = 17 / 4; /* division -- ia3 == 4 */
var ia4 = 17 /^ 4; /* ceil-division -- ia4 == 5 */
var ia5 = 25 % 7; /* modulus -- ia5 == 4 */
/* There are also addition (`+`) and subtraction (`-`) operators */
/* Bitwise
*
* Poke has the following left-associative binary bitwise operators:
* - Logical shifts (`<<.`, and `.>>`)
* - AND (`&`)
* - XOR (`^`)
* - OR (`|`)
* - Bitwise concatenation (`:::`)
*
* Right-associative unary bitwise operator:
* - Bitwise complement (`~`)
*
* NOTE There's no arithmetic right-shifting operator in Poke.
*/
var ib1 = 1 <<. 10; /* ib1 == 1024 */
var ib2 = 1024 .>> 9; /* ib2 == 2 */
var ib3 = 0x12UB ::: 0x34UB; /* ib3 == 0x1234UH */
var ib4 = ~0x0fUB; /* ib4 == 0xf0UB */
/* String values (null-terminated) */
var foobar_string = "foo\nbar";
var empty_string = "";
/* Offset values
*
* Poke does not using integers to specify offsets in binary data, it has a
* primitive type for that: offset!
*
* Offsets have two parts:
* - magnitude (an integer)
* - unit (b (bit), byte (B), etc.)
*
* Offsets are also useful for specifying the size.
*/
/* Offsets with named units */
var off_8_bits = 8#b;
var off_23_bytes = 23#B;
var off_2000_bits = 2#Kb;
var off_2000_bytes = 2#KB;
var off_3_nibbles = 3#N; /* 3 nibbles (each nibble is 4 bits) */
var off_1_byte = #B; /* You can omit the magnitude if it's 1 */
/* Offsets with numeric units */
var off_8_8 = 8#8; /* magnitude: 8, unit: 8 bits */
var off_2_3 = 2#3; /* magnitude: 2, unit: 3 bits */
/* Offset arithmetic
*
* OFF +- OFF -> OFF
* OFF * INT -> OFF
* OFF / OFF -> INT
* OFF /^ OFF -> INT
* OFF % OFF -> OFF
*/
var off_1_plus_2 = 1#B + 2#B; /* 3#B */
var off_1_minus_2 = 1#B - 2#B; /* -1#B */
var off_8_times_10 = 8#B * 10; /* 80#B */
var off_10_times_8 = 10 * 8#B; /* 80#B */
var off_7_div_1 = 7#B / 1#B; /* 7 */ /* This is an integer */
var off_7_cdiv_2 = 7#B /^ 2#B; /* 4 */ /* This is an integer */
var off_7_mod_3 = 7#B % 3#B; /* 1#B */
/* The following units are pre-defined in Poke:
*
* b, N, B, Kb, KB, Mb, MB, Gb, GB, Kib, KiB, Mib, MiB, Gib, GiB
*
* Poke supports user-defined units using `unit` construction:
*
* unit NAME = CONSTANT_EXPRESSION;
*/
unit BIT = 1;
unit NIBBLE = 4;
unit kilobit = 10U ** 3;
unit kilobyte = 10U ** 3 * 8;
var off_bit = 10#BIT; /* off_bit == 10#b */
var off_nibble = 4#NIBBLE; /* off_nibble == 4#N && off_nibble == 16#b */
var off_kb = 1#kilobit; /* off_kb == 1#Kb && off_kb == 1000#b */
var off_kB = 2#kilobyte; /* off_kB == 2#KB && off_kB == 16000#b */
/* Array values */
var arr1 = [1, 2, 3];
var arr2 = [[1, 2], [3, 4]];
var elem10 = arr1[0]; /* Arrays are indexed using the usual notation */
var elem12 = arr1[2]; /* This is the last element of `arr1`: 3 */
/* If you try to access elements beyond the bounds, you'll get an
* `E_out_of_bounds` exception.
*/
/* var elem1x = arr1[3]; */
/* var elem1y = arr1[-1]; */
/* Array trimming: Extraction of a subset of the array */
var arr3 = arr1[0:2]; /* arr3 == [arr1[0], arr1[1]] */
var arr4 = arr1[0:0]; /* arr4 is an empty array */
/* Array is a "composite value"; It behaves like pointers on copy.
* The underlying data is shared between `arr1` and `arr5`.
*/
var arr5 = arr1;
arr5[0] = -1; /* arr1 == [-1, 2, 3] */
/* Array trimming *makes* a new array with *copies* of the selected data */
var arr6 = arr1[:];
var arr7 = arr2[:];
arr6[0] = 1; /* arr6 == [1, 2, 3] && arr1 == [-1, 2, 3] */
arr7[0][0] = -1; /* arr2 == [[-1, 2], [3, 4]] */
/* Making array using the constructor */
var arr8 = string[3]("Hi"); /* arr8 == ["Hi", "Hi", "Hi"] */
var arr9 = int<32>[](); /* arr9 == arr4 */
/* Types
*
* Before talking about `struct` values, it'd be nice to first talk about types
* in Poke.
*/
/* Integral types
*
* Most general-purpose programming languages provide a small set of integer
* types. Poke, on the contrary, provides a rich set of integer types featuring
* different widths, in both signed and unsigned variants.
*
* `int<N>` is a signed integer with `N`-bit width. `N` can be an integer
* literal in the range `[1, 64]`.
*
* `uint<N>` is the unsigned variant.
*
* Examples:
*
* uint<1>
* uint<7>
* int<64>
*/
/* String type
*
* There is one string type in Poke: `string`
* Strings in Poke are null-terminated.
*/
/* Array types
*
* There are three kinds of array types:
*
* - Unbounded: arrays that have no explicit boundaries, like `int<32>[]`
* - Bounded by number of elements, like `int<64>[10]`
* - Bounded by size, like `uint<32>[8#B]`
*/
/* Offset types
*
* Offset types are denoted as `offset<BASE_TYPE,UNIT>`, where BASE_TYPE is
* an integer type and UNIT the specification of an unit.
*
* Examples:
*
* offset<int<32>,B>
* offset<uint<12>,Kb>
*/
/* Struct types
*
* Structs are the main abstraction that Poke provides to structure data. A
* collection of heterogeneous values.
*
* And there's no padding or alignment between the fields of structs.
* In other words: WYPIWYG (What You Poke Is What You Get)!
*
* Examples:
*
* struct {
* uint<32> i32;
* uint<64> i64;
* }
*
* struct {
* uint<16> flags;
* uint<8>[32] data;
* }
*
* struct {
* int<32> code;
* string msg;
* int<32> exit_status;
* }
*/
/* User-declared types
*
* There's a mechanism to declare new types:
*
* type NAME = TYPE;
*
* where NAME is the name of the new type, and TYPE is either a type specifier
* or the name of some other type.
*
* The supported type specifiers are integral types, string type, array types,
* struct types, function types, and `any` (The `any` type is used to
* implement polymorphism).
*/
type Bit = uint<1>;
type Int = int<32>;
type Ulong = uint<64>;
type String = string; /* Just to show that this is possible! */
type Buffer = uint<8>[]; /* Unbounded array of type uint<8> */
type Triple = int<32>[3]; /* Bounded array of 3 elements */
type Buf1024 = uint<8>[1024#B]; /* Bounded array with size of 1024 bytes */
type EmptyStruct = struct {};
type BufferStruct =
struct
{
Buffer buffer;
};
type Pair_32_64 =
struct
{
uint<32> i32;
uint<64> i64;
};
type Packet34 =
struct
{
uint<16> flags;
uint<8>[32] data;
};
type Error =
struct
{
int<32> code;
string msg;
int<32> exit_status;
};
/* Now back to the values */
/* Struct values */
var empty_struct = EmptyStruct {};
type Packet =
struct
{
uint<16> flags;
uint<8>[8] data;
};
var packet_1 =
Packet
{
flags = 0xff00,
data = [0UB, 1UB, 2UB, 3UB, 4UB, 5UB, 6UB, 7UB],
};
var packet_2 =
Packet
{
flags = 1,
/* The following line is invalid; because type of numbers is `uint<32>`.
*/
/* data = [0, 1, 2, 3, 4, 5, 6, 7], */
/* User cannot specify less than 8 elements; because the `data` field is a
* fixed size array. So the following line is compilation error:
*/
/* data = [0UB, 1UB, ], */
};
var packet_3 =
Packet
{
/* flags = 0, */ /* Fields can be omitted */
/* The fifth element (counting from zero) is initialized to `128UB`;
* and all uninitialized values before that will be initialized to `128UB`,
* too.
*/
data = [1UB, .[5] = 128UB, 2UB, 3UB],
};
/* packet_3 ==
Packet{flags=0UH,data=[1UB,128UB,128UB,128UB,128UB,128UB,2UB,3UB]}
*/
type Header =
struct
{
uint<8>[2] magic;
offset<uint<32>,B> file_size;
uint<16>; /* Reserved */
uint<16>; /* Reserved */
offset<uint<32>,B> data_offset;
};
type Payload =
struct
{
uint<8> magic;
uint<32> data_length;
/* Size of array depends on the `data_length` field */
uint<8>[data_length] data;
};
/* An interesting feature of Poke is that types also can be used as units for
* offsets. The only restriction is that the type should have known size at
* compile-time.
*/
var off_23_packets = 23#Packet; /* magnitude: 23, unit: Packet */
/* Note that this is invalid and give compilation error:
*
* var off_buffer = 1#Buffer;
*
* because `Buffer` is an unbounded array and the size is unknown at
* compile-time.
*/
/* Offset arithmetic with types as unit of offsets
*/
var packet_size = 1#Packet / 1#B; /* 10 */
var two_packet_size = 2 #Packet/#B; /* 20 */
/* Struct Field Constraints
*
* It is common for struct fields to be constrained to their values to
* satisfy some conditions. Obvious examples are magic numbers, and
* specification-derived constraints.
*/
type HeaderWithMagic =
struct
{
uint<8> magic : magic == 100UB;
uint<8> version : version <= 3;
offset<uint<32>,B> data_length;
uint<8>[data_length] data;
};
/* The constraint expression should evaluate to an integer value; that value
* is interpreted as a boolean
*/
/* The following variable definition will raise an exception:
* unhandled constraint violation exception
*/
/* var hdrmagic = HeaderWithMagic {}; */
/* This will work because all field constraints are satisfied */
var hdrmagic =
HeaderWithMagic
{
magic = 100UB,
};
/* There is another way to specify the constraints: field initializers */
/* Struct Field Initializers
*
* Field initializer has two roles:
* - Introduce constraint of the form: `field == initializer_expression`
* - Initialize the field with initializer expression
*/
type HeaderWithInit =
struct
{
uint<8> magic = 100UB;
uint<8> version = 3;
offset<uint<32>,B> data_length;
uint<8>[data_length] data;
};
/* With field initializers, this is possible: */
var hdrauto = HeaderWithInit {};
/* hdrauto.magic == 100UB && hdrauto.version == 3UB */
/* The only limitation is that we cannot specify a constraint for initialized
* fields.
*/
/* Integral Structs
*/
type IntSct = struct uint<16> /* After `struct` comes an integral type */
{
/* bit-width of all fields == 16 */
uint<4> x;
uint<8> y;
uint<4> z;
};
var intsct = IntSct
{
x = 0xa,
y = 0xbc,
z = 0xd,
};
var x = intsct.x; /* x == 0xaUN (`UN` means unsigned nibble (4-bit)) */
var y = intsct.y; /* y == 0xbcUB */
var z = intsct.z; /* z == 0xdUN */
/* Compiler promotes `intsct` to integer in all contexts where an integer is
* expected.
* Integral struct is a struct that can be interpreted as an integer.
*/
var intsct_as_uint16 = intsct + 0H; /* intsct_as_uint16 == 0xabcdUH */
var intsct_as_uint16_2 = +intsct; /* intsct_as_uint16_2 == 0xabcdUH */
/* Exception Handling
*
* Poke has a mechanism to deal with errors and unexpected situations.
* Errors are communicated by exceptions. Exceptions are values of type
* `Exception` struct.
*
* One can `raise` an exception on one end, and the other can `catch` that
* exception.
*/
fun do_io = void:
{
raise Exception{ code = EC_io, msg = "Cannot read the file" };
};
try
{
do_io;
}
catch (Exception e)
{
printf ("[example-exception] code:%i32d msg:\"%s\"\n", e.code, e.msg);
}
/* Exception codes in the range `0..254` are reserved for Poke. These codes
* are defined by `EC_*` variables in `pkl-rt.pk` file.
*
* Users also can define their own exceptions by calling `exception_code`
* function.
*/
var MY_EC_EXCP = exception_code (); /* The actual value is not important */
try raise Exception{ code = MY_EC_EXCP, msg = "My error description", };
catch {};
/* Functions
*
* Functions are lexically scoped.
*/
fun func1 = (uint<32> arg0, uint<64> arg1) uint<32>:
{
return arg0 | arg1 .>> 32; /* `.>>` is bitwise shift right operator */
}
var three = func1 (1, 2L**33); /* three == 3 (and `**` is power operator) */
fun awesome = (string name) void:
{
printf ("%s is awesome!\n", name);
}
awesome ("Poke"); /* Will print "Poke is awesome!" on terminal */
var N = 10;
fun Nsquare = int<32>: /* No input parameter */
{
/* The `N` variable is captured inside the `Nsquare` function */
return N * N;
}
var Nsq = Nsquare; /* Nsq == 100 */
N = 20;
var Nsq2 = Nsquare; /* Nsq2 == 400 */
/* Functions with optional arguments
*
* Note that the value of initialization gets captured in the closure.
*/
var ten = 10;
fun double32 = (int<32> n = ten) uint<64>:
{
n = n * 2;
return n;
}
var twenty = double32 (); /* twenty == 20UL */
var another_twenty = double32; /* It's OK to omit the `()` */
var thirty = double32 (15); /* thirty == 30UL */
/* And because `ten` is lexically closed in `double32` function: */
ten = 11;
var no_more_twenty = double32; /* no_more_twenty == 22 */
ten = 10; /* double32 == 20UL */
/* Function with no output (a procedure!) */
fun packet_toggle_flag = (Packet p) void:
{
p.flags = p.flags ^ 1;
}
packet_toggle_flag (packet_1); /* packet_1.flags == 0xff01 */
/* Anonymous functions (lambdas) */
var lam1 = lambda (int x) int: { return x + 2; };
var lam_arr = [lam1, lambda (int x) int: { return x * 2; }];
var lam_r1 = lam1 (10); /* lam_r1 == 12 */
var lam_r2 = lam_arr[0] (10); /* lam_r2 == 12 */
var lam_r3 = lam_arr[1] (10); /* lam_r3 == 20 */
/* Struct Methods
*/
type Point =
struct
{
int<32> x;
int<32> y;
method norm_squared = int<32>:
{
return x*x + y*y;
}
};
var point = Point{ x = 10, y = -1 };
var point_nsq = point.norm_squared; /* point_nsq == 101 */
/* Unions
*
* Sometimes the structure of binary format can be different depending on some
* eariler fields. To describe these kinds of formats, Poke provides `union`s.
*
* The first field of `union` for which its constraints are satisfied will be
* selected.
*/
type PacketU =
struct
{
uint<8> size;
union
{
struct
{
uint<8> typ;
uint<8>[size] data;
} alt1 : size < 32;
struct
{
uint<16> typ;
uint<8>[size - 1] data;
} alt2 : size < 128;
struct
{
uint<16> typ;
uint<8> flags;
uint<8>[size - 3] data;
} alt3;
} u;
};
var packet_u_1 =
PacketU
{
size = 10,
};
var packet_u_2 =
PacketU
{
size = 64,
};
var packet_u_3 =
PacketU
{
size = 128,
};
/* isa operator: VAR isa TYPE */
var typ1_is_a_uint8 = packet_u_1.u.alt1.typ isa uint<8>;
/* typ1_is_a_uint8 == 1 */
/* Trying to access to a non-active field results in an `E_elem` exception */
try packet_u_1.u.alt2.typ = 1;
catch if E_elem
{
print ("`alt2` is not the active field in `packet_u_1.u` union\n");
}
var typ2_is_a_uint16 = packet_u_2.u.alt2.typ isa uint<16>;
/* typ2_is_a_uint16 == 1 */
var flags3 = packet_u_3.u.alt3.flags;
/* Casts
*/
var num_u32 = 1;
var num_u64 = num_u32 as uint<64>;
/* Attributes
*
* Each value has a set of attributes.
*/
/* `size` attribute */
var sizeof_num_u32 = num_u32'size; /* sizeof_num_u32 == 4#B */
var sizeof_num_u64 = num_u64'size; /* sizeof_num_u64 == 8#B */
var sbuf = BufferStruct{};
var sizeof_sbuf = sbuf'size; /* sizeof_sbuf == 0#B */
var sizeof_packet_1 = packet_1'size; /* sizeof_packet_1 == 10#B */
/* `length` attribute */
var nelem_arr1 = arr1'length; /* nelem_arr1 == 3 */
var nelem_arrx = [1, 2, 3, 4, 5, 6]'length; /* nelem_arrx == 6 */
/* For structs it's the number of fields */
var nfields_packet_1 = packet_1'length; /* nfields_packet_1 == 2 */
/* Conditionals
*
* - if-else
* - conditional expression
*/
if (num_u32 & 1)
{
/* This branch will be evaluated */
num_u32 = num_u32 | 2; /* 1 | 2 == 3 */
num_u64 = num_u64 | 4; /* 1 | 4 == 5 */
}
else
{
num_u32 = num_u32 | 8; /* 1 | 8 == 9 */
num_u64 = num_u64 | 16; /* 1 | 16 = 17 */
}
var a_true_value = num_u32 == 3 && num_u64 == 5;
var a_false_value = num_u32 == 9 || num_u64 == 17;
var hundred = a_true_value ? 100 : 200;
var thousand = a_false_value ? 200 : 1000;
/* Loops
*
* - while
* - for-in
* - try-until
*/
var i = 0;
while (1)
{
i = i + 1;
if (i == 10)
break;
}
/* i == 10 */
print "\nList of maintainers:\n";
for (i in ["jemarch", "egeyar", "jmd", "positron", "darnir", "dan.cermak",
"bruno", "ccaione", "eblake", "tim.ruehsen", "sdi1600195",
"aaptel", "m.nabipoor", "david.faust", "indu.bhagat"])
{
printf (" %s\n", i);
}
var digits = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
for (i in "0123456789")
{
digits[i - '0'] = i - '0';
}
/* digits == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] */
var digitsEven = [8, 6, 4, 2, 0];
for (i in "0123456789" where i % 2 == 0)
{
digitsEven[(i - '0') / 2] = i - '0';
}
/* digitsEven == [0, 2, 4, 6, 8] */
/* `print` and `printf`
*/
/* `print` accepts only one argument of type `string` and prints it in the
* output; it's like the `puts` function of C standard library.
*/
print ("`print` is like `puts`!\n");
/* `printf` behaves like the `printf` function of C standard library:
* one required argument of type string as the "format string" and zero or
* more arguments according to the "conversion specifiers" of the format
* string.
*
* Conversion specifiers:
* - Signed integers: i<WIDTH><BASE>
* (like `i32x`, `i23d`, `i7o`, `i19b`)
* - Unsigned integers: u<WIDTH><BASE>
* (like `u32x`, `u23d`, `u7o`, `u19b`)
* - String: s
* - Byte (or character): c
* - Pretty-printed value: v
* <DEPTH>v
* (like `v`, `0v`, `1v`, `9v`)
* - Pretty-printed value (flat mode): Fv
* <DEPTH>Fv
* (like `Fv`, `0Fv`, `1Fv`, `9Fv`)
* - Pretty-printed value (tree mode): Tv
* <DEPTH>Tv
* (like `Tv`, `0Tv`, `1Tv`, `9Tv`)
*
* WIDTH is the width of the integral value.
* DEPTH indicates how deep the print should recurse to print the child
* fields. 0 means print all fields completely.
*/
printf ("decimal (64): %i64x %u64x\n", -1, 1);
/* decimal (64): ffffffffffffffff 0000000000000001 */
printf ("decimal (32): %i32d %u32d\n", -10, 20U);
/* decimal (32): -10 20 */
printf ("hex (32): %i32x %u32x\n", -10, 20U);
/* hex (32): fffffff6 00000014 */
printf ("octal (16): %i16o %u16o\n", -10, 20U);
/* octal (16): 177766 000024 */
printf ("bin (7): %i7b %u7b\n", -10, 20U);
/* bin (7): 1110110 0010100 */
printf ("%s like in C\n", "Works");
/* Works like in C */
printf ("%c%c is a prime number\n", 0x32, 0x33);
/* 23 is a prime number */
printf ("%v\n", Point {x = 1, y = -1}); /* Default printing mode is "flat" */
/* Point {x=1,y=-1} */
printf ("%Fv\n", Point {x = 1, y = -1}); /* Flat mode */
/* Point {x=1,y=-1} */
printf ("%Tv\n", Point {x = 1, y = -1}); /* Tree mode */
/*
Point {
x=1,
y=-1
}
*/
type PointPair =
struct
{
Point first;
Point second;
};
/* Tree mode with one level depth */
printf ("%1Tv\n",
PointPair { first = Point {x = 1, y = -1},
second = Point {x = -1, y = 1}, });
/*
PointPair {
first=Point {...},
second=Point {...}
}
*/
/* Tree mode with two level depth */
printf ("%2Tv\n",
PointPair { first = Point {x = 1, y = -1},
second = Point {x = -1, y = 1}, });
/*
PointPair {
first=Point {
x=1,
y=-1
},
second=Point {
x=-1,
y=1
}
}
*/
/* Now, the most important concept in Poke: mapping! */
/* Mapping
*
* The purpose of poke is to edit "IO spaces", which are the files or devices,
* or memory areas being edited. This is achieved by **mapping** values.
*/
/* Using `open` function one can open an IO space; Poke supports the following
* IO spaces:
*
* - Auto-growing memory buffer
* - File
* - Block device served by an NDB server
*
* It has the following prototype:
*
* fun open = (string HANDLER, uint<64> flags = 0) int<32>
*/
/* open an auto-growing memory buffer */
var memio = open("*Arbitrary Name*");
/* open a file */
var zeroio = open("/dev/zero");
/* open standard input (stdin) */
var stdin = open("<stdin>");
/* open standard output (stdout) */
var stdout = open("<stdout>");
/* open standard error (stderr) */
var stderr = open("<stderr>");
/* close the IO spaces */
close(stderr);
close(stdout);
close(stdin);
close(zeroio);
/* To access to IO space we can map a value to some area using this syntax:
*
* TYPE @ OFFST
* or,
* TYPE @ IOS : OFFSET
*
* The map operator, regardless of the type of value it is mapping, always
* returns a *copy* of the value found stored in the IO space.
*/
var ai32 = uint<32>[1] @ 0#B;
var ui32num = uint<32> @ 0#B;
ai32[0] = 0xaabbccdd; /* The first 4 bytes in IO space will change. */
ui32num = 0xddccbbaa; /* But this doesn't have any effect on IO space */
uint<32> @ 0#B = 0xddccbbaa; /* This works as expected */
/* Poke values also have the `mapped` attribute.
*/
var ai32_is_mapped = ai32'mapped; /* ai32_is_mapped == 1 */
var cur_ios = get_ios; /* `get_ios` returns the ID of current IO space */
/* `iosize` function returns the size of IO space. */
var cur_sz = iosize (cur_ios);
/* Endianness
*
* Big-endian is the default endian-ness. This can be verified by the following
* expression:
*
* get_endian == ENDIAN_BIG
*
* This can be changed using `set_endian` function.
*/
set_endian(ENDIAN_LITTLE); /* get_endian == ENDIAN_LITTLE */
/* std.pk - Standard definition for poke
*
* The following types are defined as Standard Integral Types:
* - bit
* - nibble
* - uint8, byte, char, int8
* - uint16, ushort, int16, short
* - uint32, uint, int32, int
* - uint64, ulong, int64, long
*
* Standard Offset Types:
* type off64 = offset<int64,b>;
* type uoff64 = offset<uint64,b>;
*
* Conversion Functions:
* - catos Character array to string
* - stoca String to character array
* - atoi String to integer
*
* String Functions:
* - strchr Index of first occurrence of the character in string
* - ltrim Left trim
* - rtrim Right trim
*
* Sorting Functions:
* - qsort
*
* CRC Functions:
* - crc32
*
* Misc:
* var NULL = 0#B;
*/
/* Standard pickles
*
* - bmp BMP file format
* - bpf Linux eBPF instruction set
* - btf Linux BPF Type Format
* - btf-dump Utilities for dumping BTF information
* - color Standard colors for GNU poke
* - ctf CTF (Compact Type Format)
* - dwarf DWARF debugging data format
* - elf ELF (Executable and Linkable Format)
* - id3v1 ID3v1 metadata container
* - id3v2 ID3v2 metadata container
* - leb128 LEB128 (Little Endian Base 128) variable-length encoding
* - mbr MBR (Master Boot Record) partition table
* - mcr MIT CADR microcode
* - rgb24 RGB24 encoding of colors
* - time Time-related definitions for GNU poke
* - ustar USTAR file system
*
* - argp Argp like interface for Poke programs
* - pktest Facilities to write tests for pickles
*/
/* To get more familiar with Poke, look at these standard pickles:
*
* - `id3v1.pk`
*
* Things you'll see:
*
* Language constructs:
* arrays, functions, `while` loop, exceptions, field initializers,
* unions, methods, pretty printers
*
* Standard functions:
* `print`, `printf`, `rtrim`, `catos`, `stoca`, `atoi`
*
* Idiom:
* How to find the active field of a `union` (using `E_elem` exception)
*
* - `id3v2.pk`
*
* Things you'll see:
*
* Language constructs:
* integral structs, constraints, bit concatenation, optional fields
*/
/* Happy poking! */
/* Based on
*
https://kernel-recipes.org/en/2019/talks/gnu-poke-an-extensible-editor-for-structured-binary-data/
* GNU poke reference documentation (Texinfo file)
* http://jemarch.net/pokology-20200504.html
*/
learn-poke-in-y-minutes.pk.html.gz
Description: application/gzip
- [Documentation] Learn Poke in Y Minutes,
Mohammad-Reza Nabipoor <=