From ac92c5317e293b640e35fce545ac7a88907f7768 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sat, 2 Nov 2024 15:11:00 +0000 Subject: [PATCH] add existing documentation --- doc/example.asm | 241 +++++++++++++++++ doc/sample/CaesarCipher.im | 65 +++++ doc/sample/FizzBuzz.im | 20 ++ doc/sample/Person.im | 522 +++++++++++++++++++++++++++++++++++++ doc/sample/String.im | 6 + doc/sample/Sum.im | 29 +++ doc/sample/Vehicle.im | 44 ++++ doc/standard-library.txt | 91 +++++++ doc/vm-instructions.txt | 48 ++++ 9 files changed, 1066 insertions(+) create mode 100755 doc/example.asm create mode 100755 doc/sample/CaesarCipher.im create mode 100755 doc/sample/FizzBuzz.im create mode 100755 doc/sample/Person.im create mode 100755 doc/sample/String.im create mode 100755 doc/sample/Sum.im create mode 100755 doc/sample/Vehicle.im create mode 100755 doc/standard-library.txt create mode 100755 doc/vm-instructions.txt diff --git a/doc/example.asm b/doc/example.asm new file mode 100755 index 0000000..4b98c35 --- /dev/null +++ b/doc/example.asm @@ -0,0 +1,241 @@ +@msgh (net.doorstuck.test.Person) [-init(name:age:)] + ldr x0, [bp, #-1] + str x0, [self, #0] + + ldr x0, [bp, #-2] + str x0, [self, #1] + + ret +@end + +@msgh (net.doorstuck.test.Person) [-name] + ldr x0, [self, #0] + + ret +@end + + +@msgh (net.doorstuck.test.Person) [-age] + ldr x0, [self, #1] + + ret +@end + +@msgh (net.doorstuck.test.Person) [-ageInMonths] + ldr x0, [self, #1] + ldr x1, #12 + mul x0, x0, x1 + + ret +@end + +@msgh (net.doorstuck.test.Person) [-setName:] + ldr x0, [bp, #-1] + str x0, [self, #0] + + ret +@end + +@msgh (net.doorstuck.test.Person) [-setAge:] + ldr x0, [bp, #-1] + str x0, [self, #1] + + ret +@end + +@msgh (net.doorstuck.test.Person) [-setAge:in:] + ldr x0, [bp, #-1] + + ldr x1, "years" + cmp x0, x1 + b.eq $L0001 + + ldr x1, "months" + cmp x0, x1 + b.eq $L0002 + + ldr x1, "days" + cmp x0, x1 + b.eq $L0003 + + ; else + ldr x2, #0 + str x2, [self, #1] + br $L0004 + + ; if units == "years" +L0001: str x0, [self, #1] + br $L0004 + + ; if units == "months" +L0002: ldr x2, #12 + div x0, x0, x2 + str x0, [self, #1] + br $L0004 + + ; if units == "days" +L0003: ldr x2, #365 + div x0, x0, x2 + str x0, [self, #1] + + ; end +L0004: + ret +@end + +@lambda (Lx0001) [_:] + ldr x1, @ident[cout] + ldr x2, [bp, #-1] + + ldr x3, "Count is " + add x3, x3, x2 + + push x3 + msg x1, @selector[-put:] + + ret +@end + +@lambda (Lx0002) [] + ldr x1, @ident[cout] + + ldr x2, "True!" + push x2 + + msg x1, @selector[-put:] +@end + +@lambda (Lx0003) [] + ldr x1, @ident[cout] + + ldr x2, "False!" + push x2 + + msg x1, @selector[-put:] +@end + +@init + rsv #3 ; 3 local variables + /* stack layout is now this: + bp[1] = p1 + bp[2] = i + bp[3] = j + */ + + ; p1 = Person new(name:"John Doe", age:34) + ldr x0, #34 + push x0 + + ldr x0, "John Doe" + push x0 + + ob.c x1, x1 + msg x1, @selector[-init(name:age:)] ; x0 = new Person object + str x1, [bp, #1] ; p1 = new Person object + + ; p1 setAge:100 in:"months" + + ; p1 is already loaded in x1, no need to load it again + ldr x2, @selector[-setAge:in:] + + ldr x3, "months" + push x3 + + ldr x3, #100 + push x3 + + msg x1, @selector[-setAge:in:] ; x0 = null + + ldr x3, #0 + str x3, [bp, #2] ; i = 0 + + ; (while i < 10) begin +L0001: ldr x2, @ident[cout] ; x5 = cout + ldr x4, "Count is " + + add x4, x4, x3 ; append i (in x4) to "Count is " (in x7) + + push x4 + msg x2, @selector[-put:] + + add x3, x3, #2 + + cmp x3, #100 + b.lt $L0001 + ; (while i < 0) end + + ; for i in 0 to:100 step:2 + ldr x4, #2 + push x4 + + ldr x4, #100 + push x4 + + ldr x4, #0 + + msg x5, @selector[-to:step:] + mov x5, x0 ; x5 = (0 to:100 step:2) -> iterator + + ; (for i in 0 to:100 step:2) begin +L0002: it.v x5 ; is iterator still valid? + b.z $L0003 + + it.g x4, x5 ; get current iterator value + + ldr x6, "Count is " + add x6, x6, x4 ; append i (in x4) to "Count is " (in x6) + + ldr x7, @ident[cout] + + push x6 + msg x7, @selector[-put:] + + it.n x5 ; Advance iterator + br $L0002 + ; (for i in 0 to:100 step:2) end + +L0003: ob.e x5 ; Clean up iterator + + lam.c x5, @lambda[Lx0001] ; x5 = [ _:i | cout put:'Count: {i}' ], + + push x5 + + ldr x5, #2 + push x5 + + ldr x5, #100 + push x5 + + ldr x5, #0 + msg x5, @selector[-to:step:do:] + + ldr x5, #32 + str x5, [bp, #3] ; (x5) j = 32 + + ldr x6, [bp, #1] ; (x6) i + + c.lt x5, x6, x5 + + lam.c x6, @lambda[Lx0003] + push x6 + + lam.c x6, @lambda[Lx0002] + push x6 + + msg x5, @selector[-if:else:] + + ldr x5, [bp, #2] ; (x5) j + ldr x6, [bp, #1] ; (x6) i + + cmp x6, x5 + b.ge $L0004 + + ldr x5, "True!" + push x5 + + ldr x5, @ident[cout] + + msg x5, @selector[-put:] + +L0004: ret +@end diff --git a/doc/sample/CaesarCipher.im b/doc/sample/CaesarCipher.im new file mode 100755 index 0000000..1bf92fd --- /dev/null +++ b/doc/sample/CaesarCipher.im @@ -0,0 +1,65 @@ +package net.doorstuck.test + +cout put:'Encode a string with a Caesar cipher.' + +message = '' + +while true do + cout print:'Message to encode: ' + cout flush + + cin get:message + + if message != '' then + break + end +end + +shiftWidth = 0 + +while true do + cout print:'Shift size: ' + cout flush + + shiftStr = '' + cin get:shiftStr + + if shiftStr == '' then + continue + end + + try + shiftWidth = Int parse:shiftStr + catch (#err:number_format, err) + continue + end + + break +end + +encodedMessage = '' + +for c in message do + i = c toOrdinal + sub = 0 + + if i >= 65 && i <= 90 then + sub = 65 + elif i >= 97 && i <= 122 then + sub = 97 + else + continue + end + + i -= sub + i += shiftWidth + + if i >= 26 then + i -= 26 + end + + c2 = i toChar + encodedMessage += c2 +end + +cout put:encodedMessage diff --git a/doc/sample/FizzBuzz.im b/doc/sample/FizzBuzz.im new file mode 100755 index 0000000..fa0b9e8 --- /dev/null +++ b/doc/sample/FizzBuzz.im @@ -0,0 +1,20 @@ +for i in 0 to:100 do + p = false + + if i % 3 == 0 then + cout print:'fizz' + p = true + end + + if i % 5 == 0 then + cout print:'buzz' + p = true + end + + if !p then + cout print:'{i}' + end + + cout cr +end + diff --git a/doc/sample/Person.im b/doc/sample/Person.im new file mode 100755 index 0000000..9c0b22d --- /dev/null +++ b/doc/sample/Person.im @@ -0,0 +1,522 @@ +/* example.im - An example Ivy Implementation source file */ + +package net.doorstuck.test + +use std.io + +-- Anything after a double hypen is ignored by the compiler +/* + Multi + Line + Comment +*/ + +/** + classes also double up as protocols. + + any class can "pretend" to be another class by responding to the same + messages that the base class understands. + + for example, by implementing all the messages that the Package class + understands, your class is said to implement the Package protocol, + allowing package-specific syntax (i.e. indexing and dot syntax) + to be used with your class. + + there are two standard messages that complement this behaviour: + -is: takes a Class parameter, and returns true if the recipient + is an instance of the given Class. + -implements: takes a Class parameter, and returns true if the recipient + understands all of the messages that the parameter class + understands. + **/ +class Person + /** + every message handler can have one or both of the following: + - a message name (in this case, 'init') + - a set of one or more parameters (wrapped in parentheses if the message + has a name) + + each parameter has an internal name and an (optional) label, specified + as a pair of identifiers separated by a colon (:). the label comes + first, and is the name used by whoever is sending the message the + internal name comes second, and is used by the message handler itself as + a variable name. + + you can omit the label for a parameter by specifying an underscore (_) + as the label, but this is not recommended. it looks weird, it obscures + the meaning of the message, and is really only supported for + compatibility with lambdas. + **/ + - init(name:name age:age) + self.name = name + self.age = age + end + + - test(param:data _:extra) + cout put:'Received {data}, {extra}' + end + + - name + ^self.name + end + + - age + ^self.age + end + + - ageInMonths + ^self.age * 12 + end + + - setName:name + self.name = name + end + + - setAge:age + self.age = age + end + + - setAge:age inUnit:units + match units in + #years => self.age = age, + #months => self.age = age / 12, + #days => self.age = age / 365, + _ => self.age = 0, + end + end + + - getAgeInUnit:units + ^match units in + #years => age, + #months => age / 12, + #days => age / 365, + _ => 0, + end + end + + $ exampleProperty + - get + ^42 + end + end +end + +p1 = Person new(name:'John Doe' age:34) +p1 setAge:100 inUnit:#months + + +/** + when sending a message with unlabeled parameters, the preceding colon (:) is + still required. this is part of the reason why unlabeled parameters are + weird and not recommended. + **/ +p1 test(param:'Hello' :'World') + +/******************************************************************************/ + +i = 0 +while i < 100 do + cout put:'Count is {i}' + i += 2 +end + +for i in 0 to:100 step:2 do + cout put:'Count is {i}' +end + +/** + a lambda's parameters are *always* unlabeled, and the underscore (_) before + the label colon (:) is unnecessary. + **/ +0 to:100 step:2 do:[ :i | cout put:'Count: {i}' ] + +/******************************************************************************/ + +/** + lambdas can capture state from the context in which they are created. + + a lambda can reference any local variable that is accessible at the point + it is created. if a variable from such a context is referenced, its value + is captured at the point the lambda is constructed. the value of the + variable is copied into the lambda. + + this means that, if a lambda references a variable in the outer context, + and the value of that variable changes between the lambda being created + and the lambda being executed, that change will not be reflected within + the lambda. + + because the value of captured variables is copied into the lambda, it is + safe for a method to return a lambda that references variables in its + local context. + **/ +q = 32 +l = [ cout put:'Value of q is {q}' ] /* value of `q` is captured here */ +q = 64 +_ = l call /* prints 'Value of q is 32' */ + +/******************************************************************************/ + +j = 32 + +/** + expressions are terminated with a newline. + an expression can be written across multiple lines by using + the _ (underscore) line continuation character. + **/ + +/** + keyword messages have a lower precedence than all non-assignment + binary operators, so (i < j) will be evaluated first, and + if:else: will be sent to the result. + **/ + +i < j + if:[ cout put:'True!' ] + else:[ cout put:'False!' ] + +if i < j then + cout put:'True!' +end + +/******************************************************************************/ + +package = {} + +/** + Packages can be indexed via integer or string. If a package is index with a + variable, the value of the variable is used as the index. + **/ + +package[0] = 16 + +/* All of these accesses are equivalent */ +package['x'] = 32 +package.x = 32 + +index = 'x' +package[index] = 32 + +/** + this syntax, and the pkg.* instructions that it translates to, is + implemented behind-the-scenes by sending messages to the package. + + if your custom object implements this protocol, package syntax can be used + on it too. + **/ + +/** + these are the same packages used to store Classes and global variables. + for example, the std package is a regular package object, and you can use + package syntax on it or even send it messages. + **/ + +/******************************************************************************/ + +/* integer constants can be written in all sorts of interesting formats */ +i = 100 +i = 0x100 +i = 0200 +i = 0b1010101 +i = 1_000_000_000 + +/* tuples can be used to create a set of values */ + +tuple = (32, 'a string') + +/** + they are similar to packages, except they only support consecutive integer + indices, and you cannot specify the indices when creating a tuple. + + the main purpose of tuples is to allow unwrapping of iterator variables in + for loops. + **/ + +for (key, val) in package do + cout put:'{key} -> {val}' +end + +/** + any object can be the target of a for-loop, as long as it meets one of the + following criteria: + a) is an Iterator object-- or + b) implements the Iterator protocol-- or + c) understands the -iterator message, and returns an object that itself + conforms to a) or b) + **/ + +/******************************************************************************/ + +/** + underscore (_) is used as a placeholder during assignment. + it can only appear on the left of an assignment, and anything + assigned to it is discarded. +*/ +_ = 3 * 2 + +/* it is mostly used with pattern-matching and destructuring. */ + +a = (32, 64) +(_, v) = a + +/* v is now equal to 64 */ + + +/******************************************************************************/ + +try + v = Int parse:'342' +catch (#err:number_format, err) in + cout put:'Cannot parse integer string ({err msg})' +catch (_, err) in + cout put:'Unknown error occurred ({err msg})' +end + +/* equivalent 'pure' syntax */ + +/** + this example also demonstrates how line continuations are not always + necessary when breaking a statement up over multiple lines. + + the compiler uses a few conditions to determine if it should continue + parsing a particular statement past a line break: + + 1) if the next token immediately after a line break is a label. + 2) if the token immediately before a line break if a binary operator. + 3) if the line break occurs within a set of sub-expression delimiters + (parentheses), lambda delimiters (brackets), package delimiters + (braces), or string delimiters (single or double quotes). + + note that if a line break is encountered in a string constant, + the line break character is included in the string. to prevent + this behaviour, you can precede the line break with a line continuation + character. + + because of the implicit line continuations provided by these conditions, + the following statement requires no explicit line continuations to + be correctly parsed. + **/ + +[ v = Int parse:'342' ] + on:#err:number_format do:[ :err | + cout put:'Cannot parse integer string ({err msg})' + ]; + onError:[ :err | + cout put:'Unknown error occurred ({err msg})' + ]; + call + +/** + this example doesn't meet any of the conditions required for implicit + line continuations to be inserted. because of this, it will be treated + as four separate statements. + + there are a few ways this could be fixed: + + 1) surrount the right-hand side of the statement with parentheses. + 2) put a line continuation character at the end of all but the last line. + **/ +v = 5 + squared + squared + squared + +/** + below are some examples of throwing errors. when an error is thrown, + the runtimes unwinds the stack looking for the nearest error handler. + if no error handlers are found, the exception information is written + to cerr and exection stops. + + the exact behaviour of a throw statement depends on what is thrown: + 1) a string is thrown: a generic Error object is created. + Sending -msg to the Error returns the thrown string. + Sending -data to the Error returns null. + 2) an object that implements the Error protocol is thrown: the + thrown object becomes the error object that is passed to the + error handler (or that is written to cerr when execution stops). + 3) an object that doesn't implement the Error protocol is thrown: + a generic Error object is created. + sending -msg to the Error returns null. + sending -data to the Error returns the thrown object. + **/ + +/* example 1) */ +throw 'an error occurred') + +/* example 2) */ +throw { i => 32, s => 'error' } + +/******************************************************************************/ + +x = 32 +/* a whole bunch of ways of doing conditionals using lambdas and messages */ +[ cout put:'Hello, world!' ] if:x > 10 +[ cout put:'Hello, world!' ] unless:x <= 10 + +/* and the equivalent 'fancy' syntax */ + +cout put:'Hello, world!' if x > 10 +cout put:'Hello, world!' unless x <= 10 + +/** + NOTE that keywords (if, end, try, and so on) can still be used as message + parameter names. however, they cannot be used as message names. + **/ + +/******************************************************************************/ + +/** + Order of execution (precedence) for expression elements (listed from highest + to lowest precedence). elements that appear on the same list item have equal + precedence. in these cases, their associativity determines the order of + execution. + + - unary operators + - boolean NOT (!) + - bitwise NOT (~) + - unary messages, complex messages + - binary operators + - multiplication (*) + - division (/) + - modulo (%) + - addition (+) + - subtraction (-) + - bitwise shift (<<, >>) + - (in)equality (==, !=) + - bitwise AND (&) + - bitwise XOR (^) + - bitwise OR (|) + - boolean AND (&&) + - boolean OR (||) + - cascade (;) + - inline if-else + - keyword messages + - binary operators + - assignment by product, quotient, and remainder (*=, /=, %=) + - assignment by sum and difference (+=, -=) + - assignment by bitwise shift (<<=, >>=) + - assignment by bitwise AND, XOR, and OR (&=, ^=, |=) + - assignment (=) + **/ + +p1 + setAge:2 squared squared + 4 squared multiply(by:2 add:4 + 1 * 3) + in:'mon' + 'ths' +(p1 + setAge:(((2 squared) squared) + ((4 squared) multiply(by:2 add:(4 + (1 * 3))))) + in:('mon' + 'ths')) + +/******************************************************************************/ + +/* how about package comprehension? */ + +/** + these two lines both construct a package containing all even numbers between + 0 and 100 incremented by 1 + + the first one uses fancy list/package comprehension syntax. + the second one uses pure message/lambda syntax. + **/ +data = { x + 1 for x in 0 to:100 if (x % 2) == 0 } +data = (0 to: 100) select:[ :x | ^(x % 2) == 0 ] collect:[ :x | ^x + 1 ] + +/** + for a package comprehension that follows this template: + EXPRESSION for VARIABLE in COLLECTION if CONDITION + the equivalent -select:collect: message would look something like this: + COLLECTION select:[ VARIABLE | CONDITION ] collect:[ VARIABLE | EXPRESSION ] + + -select:collect: is used to create a new package by filtering and + transforming an existing package. + + To apply a filter without a subsequent transform, you can use -select: + To apply a transformation with a filter beforehand, you can use -map: + **/ + +/******************************************************************************/ + +/** + packages also support a -map: message for constructing a new package by + manipulating an existing one. + **/ + +/** + this example creates a package of all ints between 1 and 5 using a regular + collection literal, and then uses -map: to create a second package by + doubling each of the numbers in the fist package. + **/ +pkg1 = { 1, 2, 3, 4, 5 } +pkg2 = { x * 2 for x in pkg1 } +pkg2 = pkg1 map:[ :x | ^x * 2 ] + +/******************************************************************************/ + +/** + semicolon (;) is the cascade operator. it returns the recipient of the + previously sent message. it can be used to send multiple messages to a + single recipient. + **/ + +age = Person new(name:'John Doe' age:34); + setAge:144 inUnit:TimeUnit.MONTHS; + ageInMonths + +-- age now has the value 144 +-- the same behaviour can be achieved by using do..end + +age = do + x = Person new(name:'John Doe' age:34) + x setAge:144 inUnit:TimeUnit.MONTHS + x ageInMonths +end + +/** + this allows messages to be easily chained, without relying on the message + handler returning `self`. + **/ + +/** + NOTE that cascade is a binary operator, and cannot be used without a + right-hand operand. otherwise, simply adding a semicolon to the end of an + expression would change the behaviour (and result) of the expression. + + however, because cascade is a binary operator, it also supports implicit + line continuations. + + for situations where you want to return the recipient after a chain of + cascaded messages, you can use the -yourself message, which is understood by + all objects by default and always returns the object itself. + **/ + +p1 = Person new(name:'John Doe' age:34); + setAge:100 inUnit:TimeUnit.MONTHS; + yourself + +/* p1 now contains the Person object */ +/* again, with do..end */ + +p1 = do + x = Person new(name:'John Doe' age:34) + x setAge:100 inUnit:TimeUnit.MONTHS + x +end + +/******************************************************************************/ + +/** + Blocks are a bit like lambdas, except they are part of the context of the + code that uses them. They are essentially a way of evaluating multiple + statements in a place where a single statement is usually expected. + **/ + +/* Blocks are delimited by do..end, and the return operator can be used within + them. When it is, the value that is 'returned' will become the value that + the block as a whole evaluates to. also, the result of the last expression + evaluated in a block will be taken as its overall value. */ +val = do + x = 3 + y = 2 + x * y +end + +/* after this statement is executed, `val` will be equal to 6. */ + diff --git a/doc/sample/String.im b/doc/sample/String.im new file mode 100755 index 0000000..a9323f2 --- /dev/null +++ b/doc/sample/String.im @@ -0,0 +1,6 @@ +package net.doorstuck.test + +s1 = 'hello world' + +s2 = s1 map:[ :c | ^c uppercase ] +s3 = s1 map:[ :c | ^((c ordinal) + 1) toChar ] diff --git a/doc/sample/Sum.im b/doc/sample/Sum.im new file mode 100755 index 0000000..f6557d7 --- /dev/null +++ b/doc/sample/Sum.im @@ -0,0 +1,29 @@ +package net.doorstuck.test + +cout put:'Finds the sum of a set of numbers.' + +sum = 0 + +while true do + cout print:'Number (blank to finish): ' + cout flush + + input = '' + v = 0 + cin get:input + + if input == '' then + break + end + + try + v = Int parse:input + catch (#err:number_format, err) + cout put:'{input} is not a valid number.' + continue + end + + sum += v +end + +cout put:'Sum: {sum}' diff --git a/doc/sample/Vehicle.im b/doc/sample/Vehicle.im new file mode 100755 index 0000000..1005b8e --- /dev/null +++ b/doc/sample/Vehicle.im @@ -0,0 +1,44 @@ +package net.doorstuck.vehicles + +protocol Vehicle + - startup + - shutdown + - nrWheels + - nrDoors + - turn(direction:dir) + - accelerate(direction:dir) + - velocity +end + +class Car + - startup + self.engineRunning = true + end + + - shutdown + self.engineRunning = false + end + + - nrWheels + ^4 + end + + - nrDoors + ^4 + end + + - turn(direction:dir) + self.currentDirection = dir + end + + - accelerate(direction:dir) + match dir + #forward => self.currentVelocity += 1, + #backward => self.currentVelocity -= 1, + end + end + + - velicity + ^self.currentVelocity + end +end diff --git a/doc/standard-library.txt b/doc/standard-library.txt new file mode 100755 index 0000000..fc596ab --- /dev/null +++ b/doc/standard-library.txt @@ -0,0 +1,91 @@ +vim: tabstop=2 shiftwidth=2 expandtab + +*** PACKAGES ****************************************************************** + + ## std.lang ##### + This package contains all of the "built-in" classes, such as String, + Package, etc. + + This package is always included by default for all Ivy source files, so + there is no need to include a `use std.lang` statement. + + + ## std.io ##### + This package includes classes for performing IO operations, including + reading/writing to the terminals, files, etc. + + This package also includes the cin, cout, and cerr objects that point + to standard in, standard out, and standard error respectively. + + +*** CLASSES ******************************************************************* + +//// std.lang //// + + ## Object ##### + -is: + -implements: + -respondsTo: + -doesNotUnderstand(selector:args:) + -sendMsg(selector:args:) + -toString + + ## Int ##### + +parse: + -to: + -to:step: + -to:step:do: + + ## Enum ##### + ** + + ## String ##### + -size + -uppercase + -lowercase + -append: + -prepend: + -at:insert: + -contains: + -iterator + + ## Bool ##### + -if: + -if:else: + + ## Package ##### + -size + -exists: + -at:put: + -at: + -map: + -select: + -select:collect: + -iterator + + ## Iterator ##### + -value + -moveNext + -select: + -select:collect: + -iterator + + ## Lambda ##### + -argCount + -call + -call: + -call(_:_:) + -call(_:_:_:) + -call(_:_:_:_:) + -call(args:) + -on:do: + -if: + -unless: + + ## Selector ##### + +parse: + + ## Error ##### + -msg + -data + -withMsg:andData: diff --git a/doc/vm-instructions.txt b/doc/vm-instructions.txt new file mode 100755 index 0000000..55faf00 --- /dev/null +++ b/doc/vm-instructions.txt @@ -0,0 +1,48 @@ +ldr +ldr +ldr +ldr +ldr +ldr + +str +str +str + +push +pop + +add +sub +mul +div + +cmp + +c.eq +c.ne +c.lt +c.le +c.gt +c.ge + +br +b.z +b.nz +b.eq +b.ne +b.lt +b.le +b.gt +b.ge + +ob.c +ob.e + +lam.c + +it.g +it.n +it.v + +ret