├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── examples ├── coroutine.q ├── fibonacci.q ├── noint.q └── wait-until.q ├── quaint.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ └── quaint.xcscheme ├── src ├── ast.c ├── ast.h ├── codegen.c ├── codegen.h ├── common.h ├── exec.c ├── exec.h ├── htab.c ├── htab.h ├── lex.c ├── lex.h ├── main.c ├── parse.c ├── parse.h ├── scope.c ├── scope.h ├── type.c └── type.h └── tests ├── ast.inappropriate-context.q ├── ast.inappropriate-wexp.q ├── ast.invalid-types.q ├── lex.unknown-token-end.q ├── lex.unknown-token.q ├── parse.incomplete-expr.q ├── parse.missing-brace.q ├── parse.missing-expr.q ├── parse.missing-op.q ├── parse.missing-paren.q ├── parse.missing-semi.q ├── q.q ├── scope.duplicates.q ├── type.args-mismatch.q ├── type.binop-mismatch.q ├── type.forbidden-pointer-arith.q ├── type.lvalue-required.q ├── type.return-mismatch.q └── type.wait-label-mismatch.q /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | quaint.xcodeproj/project.xcworkspace/ 3 | quaint.xcodeproj/xcuserdata/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Blagovest Buyukliev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS := -std=c11 -D_ISOC11_SOURCE -D_POSIX_C_SOURCE=200809L \ 2 | -fno-signed-char -fno-strict-aliasing 3 | 4 | ifeq ($(DEBUG), 1) 5 | CFLAGS += -g3 -DDEBUG -O0 6 | else 7 | CFLAGS += -fomit-frame-pointer -flto -DNDEBUG -O2 8 | endif 9 | 10 | ifeq ($(32BIT), 1) 11 | CFLAGS += -m32 12 | LDFLAGS := -m32 13 | else 14 | CFLAGS += -m64 15 | LDFLAGS := -m64 16 | endif 17 | 18 | CFLAGS += -Wall -Werror -Wno-uninitialized 19 | CC_IS_CLANG := $(findstring clang, $(shell $(CC) --version)) 20 | 21 | ifdef CC_IS_CLANG 22 | CFLAGS += -Wextra -pedantic \ 23 | -Wsign-conversion \ 24 | -Wconversion \ 25 | -Wshadow \ 26 | -Wno-gnu-designator \ 27 | -Wno-gnu-conditional-omitted-operand \ 28 | -Wno-gnu-statement-expression \ 29 | -Wno-gnu-zero-variadic-macro-arguments 30 | endif 31 | 32 | SRCDIR := ./src 33 | OUTDIR := ./build/make 34 | OBJDIR := $(OUTDIR)/obj 35 | EXNAME := $(OUTDIR)/quaint 36 | DEPFILE := $(OUTDIR)/.deps 37 | 38 | SRCS := $(wildcard $(SRCDIR)/*.c) 39 | OBJS := $(addprefix $(OBJDIR)/, $(notdir $(SRCS:.c=.o))) 40 | 41 | all: $(EXNAME) 42 | 43 | depend: $(DEPFILE) 44 | 45 | $(DEPFILE): $(SRCS) 46 | @mkdir -p $(OUTDIR) 47 | $(CC) $(CFLAGS) -MM $^ | sed -E 's|([a-z]+)\.o|$(OBJDIR)/\1\.o|' > $(DEPFILE) 48 | 49 | -include $(DEPFILE) 50 | 51 | $(OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.c 52 | @mkdir -p $(OBJDIR) 53 | $(CC) $(CFLAGS) -c $< -o $@ 54 | 55 | $(EXNAME): $(OBJS) 56 | $(CC) -o $(EXNAME) $^ $(LDFLAGS) 57 | 58 | .PHONY: clean 59 | 60 | clean: 61 | rm -rf $(OUTDIR) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Quaint programming language 2 | =============================== 3 | 4 | An experimental, static, strongly typed, procedural, VM-based, memory-unsafe 5 | language with first-class resumable functions. 6 | 7 | Quaint inherits the memory model and operational semantics of C, adding some 8 | modernised "21st century" syntactic features, a strong typing discipline, 9 | built-in types of an exact specified width, well-defined behaviour w.r.t. struct 10 | padding, and seamless support for resumable functions. The runtime is currently 11 | implemented as a GP-register-free, memory-to-memory machine. The performance 12 | should be decent enough for most non-CPU-intensive tasks. 13 | 14 | This is a work in progress. Currently, the language does not aim to be rich of 15 | convenience "sugary" features, nor be "fast" nor ready for practical use. It 16 | rather attempts to fix some of the murky aspects of C – namely, the optional 17 | curly braces, the arcane type declaration syntax, the frustrating implicit 18 | integer conversion and promotion rules, the confusing array-to-pointer decaying. 19 | 20 | The main goal of Quaint is to demonstrate the viability of a new idiom for 21 | "sane" concurrent programming that bears resemblance to promises and generators. 22 | 23 | In essence, every function can be called either synchronously (just as in C) or 24 | asynchronously via a `quaint` object. The called function does not have any 25 | explicit "yield" statements, but merely only optional "wait labels" that mark 26 | eventual suspension points in which the caller might be interested in. The 27 | caller decides whether to wait with a timeout or wait until reaching a certain 28 | "wait label". The caller can also query the quaint to see whether it has lastly 29 | passed a particular wait label. The callee can have optional `noint` blocks of 30 | statements during which it cannot be interrupted even if the caller is waiting 31 | with a timeout and that timeout has elapsed. 32 | 33 | Quaint is entirely single-threaded – asynchronous functions run only when the 34 | caller is blocked on a `wait` statement or on a "run-till-end & reap-value" 35 | operator. During all other times, the resumable function is in a "frozen" state 36 | which holds its execution context and stack. 37 | 38 | ## Table of contents 39 | 40 | * [Building](#building) 41 | * [Basic syntax](#basic-syntax) 42 | * [Significant differences from C](#c-differences) 43 | * [Built-in type table](#built-in-type-table) 44 | * [Operator table](#operator-table) 45 | * [Built-in functions](#built-in-functions) 46 | * [The interesting part: resumable functions](#resumable-functions) 47 | * [What is a `quaint()`?](#what-is-a-quaint) 48 | * [The `~` "quaintify" operator](#tilde-operator) 49 | * [The `@` query operator](#at-operator) 50 | * [The `wait` statement](#wait-stmt) 51 | * [The `*` "run-till-end & reap-value" operator](#rterv-operator) 52 | * [Wait labels](#wait-labels) 53 | * [The `noint` block](#noint-block) 54 | * [An example](#an-example) 55 | * [Currently lacking features compared to C](#lacking-features) 56 | * [Future directions](#future-directions) 57 | * [Feedback](#feedback) 58 | 59 | 60 | ## Building 61 | 62 | `make` should succeed on Linux and OS X with a recent-enough version of either 63 | Clang or GCC. Additionally, an Xcode project can be built and debugged under OS 64 | X. When building with make, the executable will be put in `./build/make/quaint`. 65 | When building with Xcode, the executable will be in `./build/xcode/quaint`. 66 | 67 | Note: building with the `-flto` link-time optimisation flag tends to fail on 68 | some older GCC versions. GCC seems to do some improper optimisations at `-O2`, 69 | too. Prefer Clang for building. 70 | 71 | To try it, type `./build/make/quaint ./examples/fibonacci.q`. 72 | 73 | Other Unixes have not been tested, but Quaint should very likely be able to work 74 | there as it depends only on the C standard library and POSIX system calls. 75 | 76 | The Makefile supports a few parameters: 77 | 78 | * `make` with no parameters builds the project in release mode (assertions 79 | removed) with `-O2` and link-time optimisation, which is rather slow 80 | * `make DEBUG=1` builds it with no optimisations and assertions turned on 81 | * `make 32BIT=1` builds it as a 32-bit executable 82 | 83 | 84 | ## Basic syntax 85 | 86 | Curly braces are mandatory and no parentheses are required around the conditions 87 | of control-flow statements. The three current control-flow statements look like 88 | this: 89 | 90 | ``` 91 | if EXPR { 92 | ... 93 | } elif EXPR { 94 | ... 95 | } else { 96 | ... 97 | } 98 | 99 | while EXPR { 100 | ... 101 | } 102 | 103 | do { 104 | ... 105 | } while EXPR; 106 | ``` 107 | 108 | The variable and type declaration syntax is much more "human-oriented" than C. 109 | The equivalent of `uint8_t a = 0;` is written as follows: 110 | 111 | ``` 112 | a: byte = 0; 113 | ``` 114 | 115 | More examples: 116 | 117 | ``` 118 | arr: int[5]; /* arr is an array of 5 ints */ 119 | p: ptr(int); /* p is a pointer to an int */ 120 | p: ptr[3](int); /* p is an array of 3 pointers to int */ 121 | p: ptr[3](int[5]); /* p is an array of 3 pointers to array of 5 ints */ 122 | 123 | /* fp is a function pointer to a function taking a byte, returning an int */ 124 | fp: fptr(b: byte): int; 125 | 126 | /* point is an anonymous struct having 2 members of type u32 (alias of int) */ 127 | point: struct(x: u32, y: u32); 128 | 129 | /* q is a quaint (promise) to a value of type int */ 130 | q: quaint(int); 131 | 132 | /* q is a quaint (promise) to a value of type void */ 133 | q: quaint(); 134 | ``` 135 | 136 | Functions are defined as follows (function prototypes do not exist): 137 | 138 | ``` 139 | f1(a: uint, b: uint): ulong 140 | { 141 | const addend: ulong = 5:ulong; 142 | return a:ulong + b:ulong + addend; 143 | } 144 | 145 | /* a function with no parameters and no return value */ 146 | f2 147 | { 148 | return; 149 | } 150 | ``` 151 | 152 | Automatic variables may be declared and initialised anywhere within a lexical 153 | block. Global variables are declared only at top-level file scope. User-defined 154 | types can be defined at file scope via the `type` statement: 155 | 156 | ``` 157 | type mytype: struct( 158 | member_one: ptr(quaint(byte)), 159 | member_two: vptr, 160 | member_three: u32, 161 | member_four: fptr(x: int[12]): u64 162 | ); 163 | ``` 164 | 165 | 166 | ## Significant differences from C 167 | 168 | * Quaint is as strongly typed as it gets. Implicit type conversions are not 169 | taking place at all. All binary arithmetic and logical operations require that 170 | both operands are of the same size and signedness. Explicit casts must be used 171 | to equalise the types whenever there is a mismatch. The `as` and `:` typecast 172 | operators are aliases, except that `as` has a slightly weaker precedence. 173 | 174 | * Another peculiarity is that number literals have an unsigned integral type 175 | of the least size that is able to hold the value: 176 | 177 | | Literal Range | Type | Alias | 178 | | -------------------------------------- | ----------- | --------- | 179 | | `0` to `255` | `byte` | `u8` | 180 | | `256` to `65535` | `ushort` | `u16` | 181 | | `65536` to `4294967295` | `uint` | `u32` | 182 | | `4294967296` to `18446744073709551615` | `ulong` | `u64` | 183 | | String Literals | `ptr(byte)` | `ptr(u8)` | 184 | 185 | * String literals are null-terminated and *modifiable*, but not extensible 186 | beyond their end. 187 | 188 | * Arrays always act and look like arrays, never pretending to be pointers. The 189 | expression `&arr[0]` gets a pointer to the first element, while `&arr` gets a 190 | pointer to the entire array. The expression `arr` is of type 191 | `whatever_type[whatever_constant_size]`. Two array types are considered equal 192 | only if their types and sizes are equal. Arrays of equal types may be assigned 193 | to each other with the `=` operator. Arrays can be passed and returned by value 194 | from functions. 195 | 196 | 197 | ## Built-in type table 198 | 199 | | Type ID | Alias | Signed | Unsigned | Size | Alignment | 200 | | ----------- | ---------- | ---------- | ---------- | ---------- | ---------- | 201 | | byte | u8 | | ✓ | 1 | 1 | 202 | | sbyte | i8 | ✓ | | 1 | 1 | 203 | | ushort | u16 | | ✓ | 2 | 2 | 204 | | short | i16 | ✓ | | 2 | 2 | 205 | | uint | u32 | | ✓ | 4 | 4 | 206 | | int | i32 | ✓ | | 4 | 4 | 207 | | ulong | u64 | | ✓ | 8 | 8 | 208 | | long | i64 | ✓ | | 8 | 8 | 209 | | usize | | | ✓ | 8 | 8 | 210 | | ssize | | ✓ | | 8 | 8 | 211 | | uptr | | | ✓ | 8 | 8 | 212 | | iptr | | ✓ | | 8 | 8 | 213 | | ptr | | | ✓ | 8 | 8 | 214 | | vptr | | | ✓ | 8 | 8 | 215 | | fptr | | | ✓ | 8 | 8 | 216 | | quaint | | | ✓ | 8 | 8 | 217 | | struct | | | | - | - | 218 | | union | | | | - | - | 219 | | enum | | | ✓ | - | - | 220 | 221 | The types `usize` and `ssize` are preferred for pointer arithmetic (analogous 222 | to `size_t` and `ssize_t`). 223 | 224 | `uptr` and `iptr` are the counterparts of `uintptr_t` and `intptr_t`, 225 | respectively. 226 | 227 | `vptr` is a void pointer that cannot be dereferenced and cannot be used for 228 | pointer arithmetic, just like in C. The built-in constant `null` is of type 229 | `vptr`. 230 | 231 | `fptr` is a function pointer that can neither be dereferenced nor be used in 232 | pointer arithmetic. 233 | 234 | Structs and unions have an appropriate size and alignment, based on the 235 | size, alignment and order of their members. 236 | 237 | 238 | ## Operator table 239 | 240 | | Operator | Binary | Unary | 241 | | --------- | ------------------------------ | ------------------------------- | 242 | | `=` | Assignment | | 243 | | `+=` | Assignment-add | | 244 | | `-=` | Assignment-subtract | | 245 | | `*=` | Assignment-multiply | | 246 | | `/=` | Assignment-divide | | 247 | | `%=` | Assignment-modulo | | 248 | | `<<=` | Assignment-left shift | | 249 | | `>>=` | Assignment-right shift | | 250 | | `&=` | Assignment-bitwise and | | 251 | | `^=` | Assignment-bitwise xor | | 252 | | `|=` | Assignment-bitwise or | | 253 | | `:` | Typecast | | 254 | | `as` | Typecast | | 255 | | `::` | Scope resolution | | 256 | | `@` | Quaint is at label test, `u8` | | 257 | | `.` | Struct/union member | | 258 | | `->` | Deref struct/union member | | 259 | | `==` | Equality, `u8` | | 260 | | `!=` | Inequality, `u8` | | 261 | | `<` | Less than, `u8` | | 262 | | `>` | Greater than, `u8` | | 263 | | `<=` | Less or equal than, `u8` | | 264 | | `>=` | Greater or equal than, `u8` | | 265 | | `&&` | Logical and, `u8` | | 266 | | `||` | Logical or, `u8` | | 267 | | `+` | Addition | Identity (id) | 268 | | `-` | Subtraction | Arithmetic negation | 269 | | `*` | Multiplication | Pointer dereference/Quaint RTE | 270 | | `/` | Integral Division | | 271 | | `%` | Modulo | | 272 | | `<<` | Left shift | | 273 | | `>>` | Right shift | | 274 | | `&` | Bitwise and | Address of, `ptr(...)` | 275 | | `|` | Bitwise or | | 276 | | `^` | Bitwise xor | Bitwise negation (id) | 277 | | `,` | Comma (rightmost id) | | 278 | | `!` | | Logical negation (id) | 279 | | `~` | | Quaintify call/value, `quaint()`| 280 | | `++` | | Prefix/postfix increment (id) | 281 | | `--` | | Prefix/postfix decrement (id) | 282 | | `sizeof` | | Size of type, `usize` | 283 | | `alignof` | | Alignment of type, `usize` | 284 | | `()` | Function call | | 285 | | `[]` | Array subscription | | 286 | | `?:` | Conditional | 287 | 288 | The semantics and precedence levels of the operators inherited from C should be 289 | the same. If you spot any "unintuitive" precedence parsing, send me feedback. 290 | 291 | 292 | ## Built-in functions 293 | 294 | | Signature | 295 | | ---------------------------------------------------------------------------- | 296 | | `monotime: u64` | 297 | | `malloc(size: usize): vptr` | 298 | | `calloc(size: usize): vptr` | 299 | | `realloc(oldptr: vptr, newsize: usize): vptr` | 300 | | `free(ptr: vptr)` | 301 | | `ps(str: ptr(byte))` | 302 | | `pu8(num: u8)` | 303 | | `pi8(num: i8)` | 304 | | `pu16(num: u16)` | 305 | | `pi16(num: i16)` | 306 | | `pu32(num: u32)` | 307 | | `pi32(num: i32)` | 308 | | `pu64(num: u64)` | 309 | | `pi64(num: i64)` | 310 | | `pnl` | 311 | | `exit(status: i32)` | 312 | 313 | 314 | ## The interesting part: resumable functions 315 | 316 | 317 | ### What is a `quaint(...)`? 318 | 319 | A `quaint` denotes a built-in, reference-like type that stores the "frozen" 320 | execution context of a function invocation. Like a pointer, it can either have a 321 | null value (a billion-dollar mistake, sure :smiley:) or point to a function 322 | invocation. The internal structure of a `quaint` is managed by the VM and is not 323 | directly accessible to the programmer. 324 | 325 | A `quaint` type must specify a subtype in parentheses. This subtype specifies 326 | the type of the value that is expected upon termination of the asynchronous 327 | invocation. In case of invoking a function that returns no value, `quaint()` 328 | will suffice. 329 | 330 | 331 | ### The `~` "quaintify" operator 332 | 333 | When applied to a function-call expression or even a pure value, the `~` unary 334 | operator returns an appropriately allocated and initialised `quaint` with a 335 | subtype that corresponds to the type of its operand. 336 | 337 | For example, if a function `f` takes two `byte` arguments and returns an `int`, 338 | the expression `~f(3, 5)` will return a `quaint(int)`. If `g` takes an argument 339 | but does not return a value, `~g(7)` will return `quaint()`. At this point, the 340 | quaint is in a state where the arguments have been pushed to its stack and its 341 | instruction pointer points to the first instruction of the function. 342 | 343 | When `~` is applied to a value, the returned quaint merely holds that value and 344 | does not point to any function. This may seem rather pointless, but it is a nice 345 | feature since it makes pure values and function invocations interchangeable and 346 | compatible with the rest of the operators listed here. 347 | 348 | 349 | ### The `@` query operator 350 | 351 | The `@` binary operator requires that its first operand is a quaint of any 352 | subtype and its second operand is the name of a wait label. The returned value 353 | is of type `byte` (alias of `u8`) and is `1` if the quaint has lastly passed 354 | that label, `0` otherwise. 355 | 356 | Two special built-in label names exist: `start` and `end`. The expression 357 | `q@start` returns `1` only when the quaint has just been created with the `~` 358 | operator and has never been `wait`ed on. The expression `q@end` returns `1` when 359 | the quaint has reached a return statement and its value can be subsequently 360 | "reaped" without blocking. Both `q@start` and `q@end` return `1` when applied 361 | to a pure-value quaint. 362 | 363 | Within the context of a `@` operator, a wait label is specified by concatenating 364 | a function name and a label name that is defined anywhere within the scope of 365 | that function, for example `q@my_function::a_label`. 366 | 367 | When applied to a null quaint, this operator returns `0`. 368 | 369 | 370 | ### The `wait` statement 371 | 372 | The `wait` statement has six basic syntactic forms and it only has side effects, 373 | not returning a value: 374 | 375 | * `wait quaint_expr` 376 | * `wait quaint_expr noblock` 377 | * `wait quaint_expr for timeout_expr [msec|sec]` 378 | * `wait quaint_expr for timeout_expr [msec|sec] noblock` 379 | * `wait quaint_expr until function_name::label_name` 380 | * `wait quaint_expr until function_name::label_name noblock` 381 | 382 | `quaint_expr` is any expression that evaluates to a `quaint(...)` type. 383 | `timeout_expr` is an unsigned integral expression that specifies either the 384 | number of seconds when `sec` is immediately used, or the number of milliseconds 385 | when `msec` is used or omitted. 386 | 387 | During a `wait` statement, the execution of the quaint is resumed and it is run 388 | until the timeout has passed, the specified label has been reached, or the 389 | function has returned. 390 | 391 | A `wait` statement is a no-op (without side effects) in any of the following 392 | situations: 393 | 394 | * When `quaint_expr` evaluates to `null` 395 | * When `quaint_expr@end` evaluates to `1` 396 | * When `timeout_expr` evaluates to `0` 397 | * When `quaint_expr` is a pure-value quaint (`quaint_expr@start && quaint_expr@end`) 398 | 399 | The `noblock` option is currently unused and not implemented, but syntactically 400 | valid. The idea behind it is that whenever the quaint enters a blocking IO 401 | built-in function, the wait should return even if the timeout has not still 402 | passed or the label has not been reached. 403 | 404 | 405 | ### The `*` "run-till-end & reap-value" operator 406 | 407 | When applied to an expression of a quaint type, the unary `*` resumes the 408 | quaint, waits until it terminates, and returns its value. If the subtype of the 409 | quaint is void, this operator does not return a value either. 410 | 411 | The operand expression must be an lvalue, too, because `*` will also free it 412 | *and* nullify it. This operator can be thought of as a counterpart of the `~` 413 | quantify operator. The former allocates the quaint, the latter releases it. The 414 | VM does not release any orphaned quaints, so a failure to release the quaint via 415 | `*` causes a memory leak in your program. 416 | 417 | When applied over a quaint that has reached the end (`q@end`), this operator 418 | immediately returns the value (or nothing in case of `quaint()`), releases the 419 | quaint and nullifies it. 420 | 421 | When applied over a null quaint, `*` returns a zero value of the appropriate 422 | subtype or nothing in case of a void subtype. 423 | 424 | 425 | ### Wait labels 426 | 427 | A wait label is a statement that can appear anywhere within the body of a 428 | function and has the following syntax (semicolons or colons *must* be omitted): 429 | 430 | ``` 431 | [an_alphanumeric_label_name] 432 | ``` 433 | 434 | Wait labels can be guarded by control-flow statements and will fire up only when 435 | the body of that control-flow statement is executed. 436 | 437 | Duplicate wait labels are allowed within a single function; each of them will 438 | fire up with the same label name when it is reached. 439 | 440 | 441 | ### The `noint` block 442 | 443 | The `noint` block is an ordinary lexical block of statements that disables the 444 | possibility of interrupting the statement sequence by a `wait` up in the call 445 | stack. This can be thought of as a "critical section", even that Quaint is 446 | entirely single-threaded. 447 | 448 | This is usually useful when modifying some global state variables which may be 449 | shared with the caller: 450 | 451 | ``` 452 | /* swap two global variables safely, regardless of any waits up in the call stack */ 453 | noint { 454 | const temp: int = global_a; 455 | global_a = global_b; 456 | global_b = temp; 457 | } 458 | ``` 459 | 460 | 461 | ### An example 462 | 463 | ``` 464 | entry 465 | { 466 | /* 467 | * Our recursive fibonacci function has exponential complexity and is very 468 | * computationally-intensive. A synchronous call to fibonacci(32:u32) takes 469 | * about 6 seconds on my i5 MacBook Air: 470 | */ 471 | ps("Synchronous call: "), pu32(fibonacci(32 as u32)), pnl(); 472 | 473 | /* Now, call the same function asynchronously, in steps of 1 second */ 474 | fibq: quaint(u32) = ~fibonacci(32 as u32); 475 | 476 | ps("At start: "), pu8(fibq@start), pnl(); 477 | iter: u32 = 0:u32; 478 | 479 | do { 480 | wait fibq for 1000 msec; 481 | ps("Iteration "), pu32(iter++), pnl(); 482 | } while !fibq@end; 483 | 484 | ps("At end: "), pu8(fibq@end), pnl(); 485 | const value: u32 = *fibq; 486 | ps("Reaped value: "), pu32(value), pnl(); 487 | } 488 | 489 | fibonacci(number: u32): u32 490 | { 491 | if number == 0:u32 || number == 1:u32 { 492 | return number; 493 | } else { 494 | return fibonacci(number - 1:u32) + fibonacci(number - 2:u32); 495 | } 496 | } 497 | ``` 498 | 499 | The way of printing output via the comma operator is indeed rather clunky, but 500 | that will be the way until variadic functions are implemented and `printf`-like 501 | functions appear. 502 | 503 | The name `entry` is not special – Quaint currently starts execution from the 504 | first defined function. Adding parameters to that first function is currently 505 | undefined behaviour. 506 | 507 | Initialisation of global variables is syntactically supported, but currently has 508 | no effect – all global variables have zero values upon program startup. 509 | 510 | 511 | ## Currently lacking features compared to C 512 | 513 | * No `for`, `switch`, `goto`, `break` and `continue` control-flow statements 514 | * No multidimensional arrays 515 | * No `float` and `double` types 516 | * No struct/union/array initialisers or compound literals 517 | * No escape sequences within string literals 518 | * No hex, octal or binary number literals 519 | * No bit-fields in structs and unions 520 | * No variadic functions 521 | * No `const`-qualified pointer subtypes, only top-level `const` 522 | * No ability to declare a struct member that is a pointer to the same struct 523 | * The `sizeof` and `alignof` operators accept only types, not variable names 524 | 525 | 526 | ## Future directions 527 | 528 | In order to make Quaint a pleasant and usable general-purpose language, these 529 | are the things I am planning to implement in the upcoming years, in decreasing 530 | priority: 531 | 532 | * Unit-based compilation and linking 533 | * Implicit namespaces based on the name of the source file 534 | * Hygienic enums 535 | * Producing stand-alone native executables with the VM (exec.c) embedded 536 | * "Safe" (slow) and "unsafe" (fast) execution modes of the VM 537 | * Additional syntax for array/struct/union literals 538 | * A richer set of control-flow statements, probably also statement expressions 539 | * Type inference 540 | * Slightly more relaxed type checking in some contexts 541 | * More built-in functions and perhaps runtime-loadable bundles of functions 542 | * Friendlier and more descriptive error messages 543 | * Some basic optimisation passes 544 | * Debugging facilities 545 | 546 | As a firm OO-nonbeliever, I will never steer the language into OO land. Quaint 547 | will remain a small, pragmatic and conservative language that can be learned in 548 | a day by an experienced C programmer. 549 | 550 | Quaint will never adopt the idiotic and perverse callback-based concurrent 551 | programming model of Node.js, either. A part of the reason I am designing and 552 | implementing this language is my frustration with the recent proliferation of 553 | half-assed concurrent paradigms. 554 | 555 | I also think that threads should be of a very limited use (if used at all) in 556 | general-purpose programming. In the future, Quaint will support forking the VM 557 | process a number of times that corresponds to the number of CPU cores. 558 | Communication among the forked processes will happen through message queues. 559 | Shared memory regions among the processes would be used only as a last resort. 560 | 561 | No compromises will be made with the quality of the implementation, even if it 562 | takes much longer to implement things cleanly and properly. 563 | 564 | 565 | ## Feedback 566 | 567 | Feedback and discussion regarding any flaws, bugs or potentially useful and 568 | interesting features is very welcome. Any interesting example code that you came 569 | up with can be added to the examples. 570 | 571 | In addition to opening up issues in GitHub, you may contact me at 572 | blagovest.buyukliev at gmail_com. 573 | -------------------------------------------------------------------------------- /examples/coroutine.q: -------------------------------------------------------------------------------- 1 | type status: enum(exit, continue, cancel): byte; 2 | 3 | entry 4 | { 5 | stat: status = status::continue; 6 | q: quaint(ptr(byte)) = ~infinite_function(&stat); 7 | i: byte = 0; 8 | 9 | do { 10 | wait q until infinite_function::before_print; 11 | ps("In caller"), pnl(); 12 | wait q until infinite_function::after_print; 13 | 14 | if ++i == 10 { 15 | stat = status::exit; 16 | wait q; 17 | } 18 | } while !q@end; 19 | 20 | ps("Function returned: "), ps(*q), pnl(); 21 | 22 | { 23 | stat = status::cancel; 24 | q = ~infinite_function(&stat); 25 | wait q; 26 | ps("Function returned: "), ps(*q), pnl(); 27 | } 28 | } 29 | 30 | infinite_function(arg: ptr(status)): ptr(byte) 31 | { 32 | while true { 33 | [before_print] 34 | ps("In callee"), pnl(); 35 | [after_print] 36 | 37 | if *arg == status::exit { 38 | return "ended with exit"; 39 | } elif *arg == status::cancel { 40 | return "ended with cancel"; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/fibonacci.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | /* 4 | * Our recursive fibonacci function has exponential complexity and is very 5 | * computationally-intensive. A synchronous call to fibonacci(32:u32) takes 6 | * about 6 seconds on my i5 MacBook Air: 7 | */ 8 | ps("Synchronous call: "), pu32(fibonacci(32 as u32)), pnl(); 9 | 10 | /* Now, call the same function asynchronously, in steps of 1 second */ 11 | fibq: quaint(u32) = ~fibonacci(32 as u32); 12 | 13 | ps("At start: "), pu8(fibq@start), pnl(); 14 | iter: u32 = 0:u32; 15 | 16 | do { 17 | wait fibq for 1000 msec; 18 | ps("Iteration "), pu32(iter++), pnl(); 19 | } while !fibq@end; 20 | 21 | ps("At end: "), pu8(fibq@end), pnl(); 22 | const value: u32 = *fibq; 23 | ps("Reaped value: "), pu32(value), pnl(); 24 | } 25 | 26 | fibonacci(number: u32): u32 27 | { 28 | if number == 0:u32 || number == 1:u32 { 29 | return number; 30 | } else { 31 | return fibonacci(number - 1:u32) + fibonacci(number - 2:u32); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/noint.q: -------------------------------------------------------------------------------- 1 | a, b, temp: byte; 2 | 3 | entry 4 | { 5 | a = 5, b = 3; 6 | q: quaint() = ~func(); 7 | 8 | do { 9 | wait q for 1 msec; 10 | ps("OUTER iteration"), pnl(); 11 | } while !q@end; 12 | 13 | *q; 14 | } 15 | 16 | func 17 | { 18 | i: u64 = 0:u64; 19 | 20 | while ++i < 100:u64 { 21 | noint { 22 | ps("Before swap: "), pu8(a), ps(", "), pu8(b), pnl(); 23 | temp = a; 24 | a = b; 25 | b = temp; 26 | ps("After swap: "), pu8(a), ps(", "), pu8(b), pnl(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/wait-until.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | q: quaint(u64) = ~pointless_function(); 4 | 5 | wait q until pointless_function::label_a; 6 | ps("At label_a: "), pu8(q@pointless_function::label_a), pnl(); 7 | 8 | wait q until pointless_function::label_b; 9 | ps("At label_b: "), pu8(q@pointless_function::label_b), pnl(); 10 | 11 | wait q until pointless_function::label_c; 12 | ps("At label_c: "), pu8(q@pointless_function::label_c), pnl(); 13 | 14 | wait q; 15 | ps("At end: "), pu8(q@end), pnl(); 16 | 17 | ps("Result: "), pu64(*q), pnl(); 18 | } 19 | 20 | pointless_function: u64 21 | { 22 | i: u64 = 0 as u64; 23 | 24 | while ++i < 1000000:u64 { 25 | x: vptr = malloc(1024:usize); 26 | free(x); 27 | 28 | if i == 300000:u64 { 29 | [label_a] 30 | } elif i == 600000:u64 { 31 | [label_b] 32 | } elif i == 900000:u64 { 33 | [label_c] 34 | } 35 | } 36 | 37 | return i; 38 | } 39 | -------------------------------------------------------------------------------- /quaint.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2B9B76EC1CA1C9F900FA651F /* ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76DA1CA1C9F900FA651F /* ast.c */; }; 11 | 2B9B76ED1CA1C9F900FA651F /* codegen.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76DC1CA1C9F900FA651F /* codegen.c */; }; 12 | 2B9B76EE1CA1C9F900FA651F /* exec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76DF1CA1C9F900FA651F /* exec.c */; }; 13 | 2B9B76EF1CA1C9F900FA651F /* htab.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76E11CA1C9F900FA651F /* htab.c */; }; 14 | 2B9B76F01CA1C9F900FA651F /* lex.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76E31CA1C9F900FA651F /* lex.c */; }; 15 | 2B9B76F11CA1C9F900FA651F /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76E51CA1C9F900FA651F /* main.c */; }; 16 | 2B9B76F21CA1C9F900FA651F /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76E61CA1C9F900FA651F /* parse.c */; }; 17 | 2B9B76F31CA1C9F900FA651F /* scope.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76E81CA1C9F900FA651F /* scope.c */; }; 18 | 2B9B76F41CA1C9F900FA651F /* type.c in Sources */ = {isa = PBXBuildFile; fileRef = 2B9B76EA1CA1C9F900FA651F /* type.c */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 2B3922201CA1C8DC00E3201A /* CopyFiles */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = /usr/share/man/man1/; 26 | dstSubfolderSpec = 0; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 1; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 2B0A20AF1CA3E4BA0096F121 /* quaint */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = quaint; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 2B9B76DA1CA1C9F900FA651F /* ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast.c; sourceTree = ""; }; 36 | 2B9B76DB1CA1C9F900FA651F /* ast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ast.h; sourceTree = ""; }; 37 | 2B9B76DC1CA1C9F900FA651F /* codegen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = codegen.c; sourceTree = ""; }; 38 | 2B9B76DD1CA1C9F900FA651F /* codegen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = codegen.h; sourceTree = ""; }; 39 | 2B9B76DE1CA1C9F900FA651F /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; 40 | 2B9B76DF1CA1C9F900FA651F /* exec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exec.c; sourceTree = ""; }; 41 | 2B9B76E01CA1C9F900FA651F /* exec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exec.h; sourceTree = ""; }; 42 | 2B9B76E11CA1C9F900FA651F /* htab.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = htab.c; sourceTree = ""; }; 43 | 2B9B76E21CA1C9F900FA651F /* htab.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = htab.h; sourceTree = ""; }; 44 | 2B9B76E31CA1C9F900FA651F /* lex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lex.c; sourceTree = ""; }; 45 | 2B9B76E41CA1C9F900FA651F /* lex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lex.h; sourceTree = ""; }; 46 | 2B9B76E51CA1C9F900FA651F /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 47 | 2B9B76E61CA1C9F900FA651F /* parse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parse.c; sourceTree = ""; }; 48 | 2B9B76E71CA1C9F900FA651F /* parse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse.h; sourceTree = ""; }; 49 | 2B9B76E81CA1C9F900FA651F /* scope.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scope.c; sourceTree = ""; }; 50 | 2B9B76E91CA1C9F900FA651F /* scope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scope.h; sourceTree = ""; }; 51 | 2B9B76EA1CA1C9F900FA651F /* type.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = type.c; sourceTree = ""; }; 52 | 2B9B76EB1CA1C9F900FA651F /* type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = type.h; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 2B39221F1CA1C8DC00E3201A /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 2B3922191CA1C8DB00E3201A = { 67 | isa = PBXGroup; 68 | children = ( 69 | 2B9B76D91CA1C9F900FA651F /* src */, 70 | 2B0A20AF1CA3E4BA0096F121 /* quaint */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | 2B9B76D91CA1C9F900FA651F /* src */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 2B9B76DA1CA1C9F900FA651F /* ast.c */, 78 | 2B9B76DB1CA1C9F900FA651F /* ast.h */, 79 | 2B9B76DC1CA1C9F900FA651F /* codegen.c */, 80 | 2B9B76DD1CA1C9F900FA651F /* codegen.h */, 81 | 2B9B76DE1CA1C9F900FA651F /* common.h */, 82 | 2B9B76DF1CA1C9F900FA651F /* exec.c */, 83 | 2B9B76E01CA1C9F900FA651F /* exec.h */, 84 | 2B9B76E11CA1C9F900FA651F /* htab.c */, 85 | 2B9B76E21CA1C9F900FA651F /* htab.h */, 86 | 2B9B76E31CA1C9F900FA651F /* lex.c */, 87 | 2B9B76E41CA1C9F900FA651F /* lex.h */, 88 | 2B9B76E51CA1C9F900FA651F /* main.c */, 89 | 2B9B76E61CA1C9F900FA651F /* parse.c */, 90 | 2B9B76E71CA1C9F900FA651F /* parse.h */, 91 | 2B9B76E81CA1C9F900FA651F /* scope.c */, 92 | 2B9B76E91CA1C9F900FA651F /* scope.h */, 93 | 2B9B76EA1CA1C9F900FA651F /* type.c */, 94 | 2B9B76EB1CA1C9F900FA651F /* type.h */, 95 | ); 96 | path = src; 97 | sourceTree = ""; 98 | }; 99 | /* End PBXGroup section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | 2B3922211CA1C8DC00E3201A /* quaint */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = 2B3922291CA1C8DC00E3201A /* Build configuration list for PBXNativeTarget "quaint" */; 105 | buildPhases = ( 106 | 2B39221E1CA1C8DC00E3201A /* Sources */, 107 | 2B39221F1CA1C8DC00E3201A /* Frameworks */, 108 | 2B3922201CA1C8DC00E3201A /* CopyFiles */, 109 | ); 110 | buildRules = ( 111 | ); 112 | dependencies = ( 113 | ); 114 | name = quaint; 115 | productName = quaint; 116 | productReference = 2B0A20AF1CA3E4BA0096F121 /* quaint */; 117 | productType = "com.apple.product-type.tool"; 118 | }; 119 | /* End PBXNativeTarget section */ 120 | 121 | /* Begin PBXProject section */ 122 | 2B39221A1CA1C8DB00E3201A /* Project object */ = { 123 | isa = PBXProject; 124 | attributes = { 125 | LastUpgradeCheck = 0800; 126 | TargetAttributes = { 127 | 2B3922211CA1C8DC00E3201A = { 128 | CreatedOnToolsVersion = 7.3; 129 | }; 130 | }; 131 | }; 132 | buildConfigurationList = 2B39221D1CA1C8DB00E3201A /* Build configuration list for PBXProject "quaint" */; 133 | compatibilityVersion = "Xcode 3.2"; 134 | developmentRegion = English; 135 | hasScannedForEncodings = 0; 136 | knownRegions = ( 137 | en, 138 | ); 139 | mainGroup = 2B3922191CA1C8DB00E3201A; 140 | productRefGroup = 2B3922191CA1C8DB00E3201A; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | 2B3922211CA1C8DC00E3201A /* quaint */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXSourcesBuildPhase section */ 150 | 2B39221E1CA1C8DC00E3201A /* Sources */ = { 151 | isa = PBXSourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 2B9B76EC1CA1C9F900FA651F /* ast.c in Sources */, 155 | 2B9B76F01CA1C9F900FA651F /* lex.c in Sources */, 156 | 2B9B76F31CA1C9F900FA651F /* scope.c in Sources */, 157 | 2B9B76F21CA1C9F900FA651F /* parse.c in Sources */, 158 | 2B9B76F41CA1C9F900FA651F /* type.c in Sources */, 159 | 2B9B76F11CA1C9F900FA651F /* main.c in Sources */, 160 | 2B9B76ED1CA1C9F900FA651F /* codegen.c in Sources */, 161 | 2B9B76EF1CA1C9F900FA651F /* htab.c in Sources */, 162 | 2B9B76EE1CA1C9F900FA651F /* exec.c in Sources */, 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXSourcesBuildPhase section */ 167 | 168 | /* Begin XCBuildConfiguration section */ 169 | 2B3922271CA1C8DC00E3201A /* debug */ = { 170 | isa = XCBuildConfiguration; 171 | buildSettings = { 172 | ALWAYS_SEARCH_USER_PATHS = NO; 173 | CLANG_ANALYZER_NONNULL = YES; 174 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 175 | CLANG_CXX_LIBRARY = "libc++"; 176 | CLANG_ENABLE_MODULES = YES; 177 | CLANG_ENABLE_OBJC_ARC = YES; 178 | CLANG_WARN_BOOL_CONVERSION = YES; 179 | CLANG_WARN_CONSTANT_CONVERSION = YES; 180 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 181 | CLANG_WARN_EMPTY_BODY = YES; 182 | CLANG_WARN_ENUM_CONVERSION = YES; 183 | CLANG_WARN_INFINITE_RECURSION = YES; 184 | CLANG_WARN_INT_CONVERSION = YES; 185 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 186 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 187 | CLANG_WARN_UNREACHABLE_CODE = YES; 188 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 189 | CODE_SIGN_IDENTITY = "-"; 190 | COPY_PHASE_STRIP = NO; 191 | DEBUG_INFORMATION_FORMAT = dwarf; 192 | ENABLE_STRICT_OBJC_MSGSEND = YES; 193 | ENABLE_TESTABILITY = YES; 194 | GCC_C_LANGUAGE_STANDARD = gnu99; 195 | GCC_DYNAMIC_NO_PIC = NO; 196 | GCC_NO_COMMON_BLOCKS = YES; 197 | GCC_OPTIMIZATION_LEVEL = 0; 198 | GCC_PREPROCESSOR_DEFINITIONS = ( 199 | "DEBUG=1", 200 | "$(inherited)", 201 | ); 202 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 203 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 204 | GCC_WARN_UNDECLARED_SELECTOR = YES; 205 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 206 | GCC_WARN_UNUSED_FUNCTION = YES; 207 | GCC_WARN_UNUSED_VARIABLE = YES; 208 | MACOSX_DEPLOYMENT_TARGET = 10.11; 209 | MTL_ENABLE_DEBUG_INFO = YES; 210 | ONLY_ACTIVE_ARCH = YES; 211 | SDKROOT = macosx; 212 | SYMROOT = ./build/xcode; 213 | }; 214 | name = debug; 215 | }; 216 | 2B3922281CA1C8DC00E3201A /* release */ = { 217 | isa = XCBuildConfiguration; 218 | buildSettings = { 219 | ALWAYS_SEARCH_USER_PATHS = NO; 220 | CLANG_ANALYZER_NONNULL = YES; 221 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 222 | CLANG_CXX_LIBRARY = "libc++"; 223 | CLANG_ENABLE_MODULES = YES; 224 | CLANG_ENABLE_OBJC_ARC = YES; 225 | CLANG_WARN_BOOL_CONVERSION = YES; 226 | CLANG_WARN_CONSTANT_CONVERSION = YES; 227 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 234 | CLANG_WARN_UNREACHABLE_CODE = YES; 235 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 236 | CODE_SIGN_IDENTITY = "-"; 237 | COPY_PHASE_STRIP = NO; 238 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 239 | ENABLE_NS_ASSERTIONS = NO; 240 | ENABLE_STRICT_OBJC_MSGSEND = YES; 241 | GCC_C_LANGUAGE_STANDARD = gnu99; 242 | GCC_NO_COMMON_BLOCKS = YES; 243 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 244 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 245 | GCC_WARN_UNDECLARED_SELECTOR = YES; 246 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 247 | GCC_WARN_UNUSED_FUNCTION = YES; 248 | GCC_WARN_UNUSED_VARIABLE = YES; 249 | MACOSX_DEPLOYMENT_TARGET = 10.11; 250 | MTL_ENABLE_DEBUG_INFO = NO; 251 | SDKROOT = macosx; 252 | SYMROOT = ./build/xcode; 253 | }; 254 | name = release; 255 | }; 256 | 2B39222A1CA1C8DC00E3201A /* debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | CLANG_WARN_ASSIGN_ENUM = YES; 260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 261 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; 262 | CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; 263 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 264 | CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; 265 | GCC_CHAR_IS_UNSIGNED_CHAR = YES; 266 | GCC_C_LANGUAGE_STANDARD = c11; 267 | GCC_ENABLE_ASM_KEYWORD = NO; 268 | GCC_PREPROCESSOR_DEFINITIONS = ( 269 | DEBUG, 270 | COLOURLESS, 271 | ); 272 | GCC_STRICT_ALIASING = NO; 273 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 274 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 275 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 276 | GCC_VERSION = ""; 277 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 278 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 279 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 280 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 281 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 282 | GCC_WARN_PEDANTIC = YES; 283 | GCC_WARN_SHADOW = YES; 284 | GCC_WARN_SIGN_COMPARE = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 286 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 287 | GCC_WARN_UNUSED_LABEL = YES; 288 | GCC_WARN_UNUSED_PARAMETER = YES; 289 | LLVM_LTO = YES; 290 | OTHER_CFLAGS = ( 291 | "-Wall", 292 | "-Wextra", 293 | "-Wno-gnu-zero-variadic-macro-arguments", 294 | "-Wno-gnu-statement-expression", 295 | "-Wno-gnu-conditional-omitted-operand", 296 | "-Wno-gnu-designator", 297 | ); 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | SYMROOT = ./build/xcode; 300 | }; 301 | name = debug; 302 | }; 303 | 2B39222B1CA1C8DC00E3201A /* release */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | CLANG_WARN_ASSIGN_ENUM = YES; 307 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 308 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; 309 | CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; 310 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 311 | CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; 312 | GCC_CHAR_IS_UNSIGNED_CHAR = YES; 313 | GCC_C_LANGUAGE_STANDARD = c11; 314 | GCC_ENABLE_ASM_KEYWORD = NO; 315 | GCC_OPTIMIZATION_LEVEL = 2; 316 | GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; 317 | GCC_STRICT_ALIASING = NO; 318 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 319 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 320 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 321 | GCC_VERSION = ""; 322 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 323 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 324 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 325 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 326 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 327 | GCC_WARN_PEDANTIC = YES; 328 | GCC_WARN_SHADOW = YES; 329 | GCC_WARN_SIGN_COMPARE = YES; 330 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 331 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 332 | GCC_WARN_UNUSED_LABEL = YES; 333 | GCC_WARN_UNUSED_PARAMETER = YES; 334 | LLVM_LTO = YES; 335 | OTHER_CFLAGS = ( 336 | "-fomit-frame-pointer", 337 | "-Wall", 338 | "-Wextra", 339 | "-Wno-gnu-zero-variadic-macro-arguments", 340 | "-Wno-gnu-statement-expression", 341 | "-Wno-gnu-conditional-omitted-operand", 342 | "-Wno-gnu-designator", 343 | ); 344 | PRODUCT_NAME = "$(TARGET_NAME)"; 345 | SYMROOT = ./build/xcode; 346 | }; 347 | name = release; 348 | }; 349 | /* End XCBuildConfiguration section */ 350 | 351 | /* Begin XCConfigurationList section */ 352 | 2B39221D1CA1C8DB00E3201A /* Build configuration list for PBXProject "quaint" */ = { 353 | isa = XCConfigurationList; 354 | buildConfigurations = ( 355 | 2B3922271CA1C8DC00E3201A /* debug */, 356 | 2B3922281CA1C8DC00E3201A /* release */, 357 | ); 358 | defaultConfigurationIsVisible = 0; 359 | defaultConfigurationName = release; 360 | }; 361 | 2B3922291CA1C8DC00E3201A /* Build configuration list for PBXNativeTarget "quaint" */ = { 362 | isa = XCConfigurationList; 363 | buildConfigurations = ( 364 | 2B39222A1CA1C8DC00E3201A /* debug */, 365 | 2B39222B1CA1C8DC00E3201A /* release */, 366 | ); 367 | defaultConfigurationIsVisible = 0; 368 | defaultConfigurationName = release; 369 | }; 370 | /* End XCConfigurationList section */ 371 | }; 372 | rootObject = 2B39221A1CA1C8DB00E3201A /* Project object */; 373 | } 374 | -------------------------------------------------------------------------------- /quaint.xcodeproj/xcshareddata/xcschemes/quaint.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 55 | 57 | 63 | 64 | 65 | 66 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/ast.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "lex.h" 4 | struct parse_node; 5 | struct scope; 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | enum { 13 | AST_AN_VOID = 0, 14 | 15 | AST_AN_UNIT, // translation unit 16 | 17 | AST_AN_USEU, // use unit statement 18 | AST_AN_TYPE, // type declaration 19 | AST_AN_DECL, // variable declaration 20 | 21 | AST_AN_FUNC, // function 22 | AST_AN_BLOK, // lexical block 23 | AST_AN_NOIN, // noint block 24 | AST_AN_COND, // if-elif-else chain 25 | AST_AN_WHIL, // while statement 26 | AST_AN_DOWH, // do-while statement 27 | 28 | AST_AN_RETN, // return statement 29 | AST_AN_WAIT, // wait statement 30 | AST_AN_WLAB, // wait label 31 | 32 | AST_AN_BEXP, // binary expression: x OP y 33 | AST_AN_UEXP, // unary expression: OP x 34 | AST_AN_FEXP, // function-call expression: x(y) 35 | AST_AN_XEXP, // postfix expression: x OP 36 | AST_AN_AEXP, // array-subscript expression: x[y] 37 | AST_AN_TEXP, // ternary expression: x ? y : z 38 | 39 | AST_AN_NAME, // naked identifier 40 | AST_AN_NMBR, // unsigned integer number literal 41 | AST_AN_STRL, // string literal 42 | 43 | AST_AN_COUNT, 44 | }; 45 | 46 | static_assert(AST_AN_COUNT - 1 <= UINT8_MAX, ""); 47 | typedef uint8_t ast_an_t; 48 | 49 | #define ast_data(ast, _type) \ 50 | ((struct ast_##_type *const) (ast)->data) 51 | 52 | struct ast_node { 53 | ast_an_t an; 54 | const struct ast_node *parent; 55 | const struct lex_token *ltok, *rtok; 56 | uint8_t data[] __attribute__((aligned(sizeof(void *)))); 57 | }; 58 | 59 | #define STMT_BLOCK \ 60 | struct scope *scope; \ 61 | size_t stmt_count; \ 62 | struct ast_node *stmts[] 63 | 64 | struct ast_unit { 65 | STMT_BLOCK; 66 | }; 67 | 68 | struct ast_useu { 69 | struct lex_symbol unit; 70 | const struct lex_symbol *ns; 71 | }; 72 | 73 | struct ast_type { 74 | uint8_t expo: 1; 75 | const struct lex_symbol *name; 76 | const struct type *type; 77 | }; 78 | 79 | struct ast_decl { 80 | uint8_t cons: 1, expo: 1, stat: 1; 81 | size_t name_count; 82 | struct lex_symbol **names; 83 | struct type *type; 84 | struct ast_node *init_expr; 85 | }; 86 | 87 | struct ast_func { 88 | uint8_t expo: 1; 89 | const struct lex_symbol *name; 90 | size_t param_count; 91 | struct type_nt_pair *params; 92 | struct type *rettype; 93 | size_t wlab_count; 94 | 95 | struct { 96 | const struct lex_symbol *name; 97 | uint64_t id; 98 | } *wlabs; 99 | 100 | STMT_BLOCK; 101 | }; 102 | 103 | struct ast_blok { 104 | STMT_BLOCK; 105 | }; 106 | 107 | struct ast_cond { 108 | struct ast_node *if_expr, *if_block; 109 | struct ast_node *else_block; 110 | size_t elif_count; 111 | 112 | struct { 113 | struct ast_node *expr, *block; 114 | } elif[]; 115 | }; 116 | 117 | struct ast_whil { 118 | struct ast_node *expr; 119 | STMT_BLOCK; 120 | }; 121 | 122 | struct ast_dowh { 123 | struct ast_node *expr; 124 | STMT_BLOCK; 125 | }; 126 | 127 | #undef STMT_BLOCK 128 | 129 | struct ast_retn { 130 | struct ast_node *expr; 131 | }; 132 | 133 | struct ast_wait { 134 | struct ast_node *wquaint, *wfor, *wunt; 135 | uint8_t noblock: 1, units: 1; 136 | 137 | /* wunt != NULL */ 138 | const struct ast_func *func; 139 | size_t wlab_idx; 140 | }; 141 | 142 | struct ast_wlab { 143 | const struct lex_symbol *name; 144 | uintptr_t func; 145 | uint64_t id; 146 | }; 147 | 148 | struct ast_bexp { 149 | lex_tk_t op; 150 | struct ast_node *lhs; 151 | 152 | union { 153 | /* op != LEX_TK_CAST && op != LEX_TK_COLN */ 154 | struct ast_node *rhs; 155 | 156 | /* op == LEX_TK_CAST || op == LEX_TK_COLN */ 157 | struct type *cast; 158 | }; 159 | 160 | union { 161 | /* op == LEX_TK_MEMB || op == LEX_TK_AROW */ 162 | size_t member_idx; 163 | 164 | /* op == LEX_TK_ATSI */ 165 | struct { 166 | const struct ast_func *func; 167 | size_t wlab_idx; 168 | }; 169 | }; 170 | 171 | struct type *type; 172 | }; 173 | 174 | struct ast_uexp { 175 | lex_tk_t op; 176 | 177 | union { 178 | /* op != LEX_TK_SZOF && op != LEX_TK_ALOF */ 179 | struct ast_node *rhs; 180 | 181 | /* op == LEX_TK_SZOF || op == LEX_TK_ALOF */ 182 | struct type *typespec; 183 | }; 184 | 185 | struct type *type; 186 | }; 187 | 188 | struct ast_fexp { 189 | struct ast_node *lhs, *rhs; 190 | size_t arg_count; 191 | struct type *type; 192 | }; 193 | 194 | struct ast_xexp { 195 | lex_tk_t op; 196 | struct ast_node *lhs; 197 | struct type *type; 198 | }; 199 | 200 | struct ast_aexp { 201 | struct ast_node *base, *off; 202 | struct type *type; 203 | }; 204 | 205 | struct ast_texp { 206 | struct ast_node *cond, *tval, *fval; 207 | struct type *type; 208 | }; 209 | 210 | struct ast_name { 211 | const struct scope_obj *scoped; 212 | struct type *type; 213 | }; 214 | 215 | struct ast_nmbr { 216 | uint64_t value; 217 | const struct type *type; 218 | }; 219 | 220 | struct ast_strl { 221 | struct lex_symbol str; 222 | const struct type *type; 223 | }; 224 | 225 | int ast_build(const struct parse_node *, struct ast_node **); 226 | 227 | enum { 228 | AST_OK = 0, 229 | AST_NOMEM, 230 | AST_INVALID, 231 | }; 232 | 233 | void ast_destroy(const struct ast_node *); 234 | void ast_print(FILE *, const struct ast_node *, int); 235 | -------------------------------------------------------------------------------- /src/codegen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ast_node; 4 | struct type; 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | enum { 11 | CODEGEN_OP_NOP = 0, 12 | 13 | CODEGEN_OP_MOV, 14 | CODEGEN_OP_CAST, 15 | 16 | CODEGEN_OP_ADD, 17 | CODEGEN_OP_SUB, 18 | CODEGEN_OP_MUL, 19 | CODEGEN_OP_DIV, 20 | CODEGEN_OP_MOD, 21 | 22 | CODEGEN_OP_EQU, 23 | CODEGEN_OP_NEQ, 24 | CODEGEN_OP_LT, 25 | CODEGEN_OP_GT, 26 | CODEGEN_OP_LTE, 27 | CODEGEN_OP_GTE, 28 | 29 | CODEGEN_OP_LSH, 30 | CODEGEN_OP_RSH, 31 | CODEGEN_OP_AND, 32 | CODEGEN_OP_XOR, 33 | CODEGEN_OP_OR, 34 | 35 | CODEGEN_OP_NOT, 36 | CODEGEN_OP_NEG, 37 | CODEGEN_OP_BNEG, 38 | CODEGEN_OP_OZ, 39 | CODEGEN_OP_INC, 40 | CODEGEN_OP_DEC, 41 | CODEGEN_OP_INCP, 42 | CODEGEN_OP_DECP, 43 | 44 | CODEGEN_OP_JZ, 45 | CODEGEN_OP_JNZ, 46 | CODEGEN_OP_JMP, 47 | 48 | CODEGEN_OP_PUSHR, 49 | CODEGEN_OP_PUSH, 50 | CODEGEN_OP_CALL, 51 | CODEGEN_OP_CALLV, 52 | CODEGEN_OP_INCSP, 53 | CODEGEN_OP_RET, 54 | CODEGEN_OP_RETV, 55 | 56 | CODEGEN_OP_REF, 57 | CODEGEN_OP_DRF, 58 | CODEGEN_OP_RTE, 59 | CODEGEN_OP_RTEV, 60 | CODEGEN_OP_QAT, 61 | CODEGEN_OP_WAIT, 62 | CODEGEN_OP_WLAB, 63 | CODEGEN_OP_GETSP, 64 | CODEGEN_OP_QNT, 65 | CODEGEN_OP_QNTV, 66 | 67 | CODEGEN_OP_NOINT, 68 | CODEGEN_OP_INT, 69 | CODEGEN_OP_BFUN, 70 | 71 | CODEGEN_OP_COUNT, 72 | }; 73 | 74 | static_assert(CODEGEN_OP_COUNT - 1 <= UINT8_MAX, ""); 75 | typedef uint8_t codegen_op_t; 76 | 77 | enum { 78 | CODEGEN_OPD_IMM, 79 | CODEGEN_OPD_TEMP, 80 | CODEGEN_OPD_AUTO, 81 | CODEGEN_OPD_GLOB, 82 | CODEGEN_OPD_COUNT, 83 | }; 84 | 85 | static_assert(CODEGEN_OPD_COUNT - 1 < 4, ""); 86 | typedef uint8_t codegen_opd_t; 87 | 88 | struct codegen_opd { 89 | codegen_opd_t opd: 2; 90 | codegen_opd_t signd: 1; 91 | codegen_opd_t indirect: 1; 92 | 93 | union { 94 | /* opd != CODEGEN_OPD_IMM */ 95 | struct { 96 | uint64_t off, size; 97 | }; 98 | 99 | /* opd == CODEGEN_OPD_IMM */ 100 | struct { 101 | uint64_t imm, immsize; 102 | }; 103 | }; 104 | } __attribute__((packed)); 105 | 106 | struct codegen_insn { 107 | codegen_op_t op; 108 | 109 | union { 110 | struct { 111 | struct codegen_opd dst, src1, src2; 112 | } bin; 113 | 114 | struct { 115 | struct codegen_opd dst, src; 116 | } un; 117 | 118 | struct codegen_opd dst; 119 | 120 | struct { 121 | struct codegen_opd dst, loc, sp; 122 | } qnt; 123 | 124 | struct { 125 | struct codegen_opd dst, val; 126 | } qntv; 127 | 128 | struct { 129 | struct codegen_opd dst, quaint; 130 | uintptr_t func; 131 | uint64_t wlab_id; 132 | } qat; 133 | 134 | struct { 135 | struct codegen_opd quaint, timeout; 136 | uintptr_t func; 137 | uint64_t wlab_id; 138 | uint8_t noblock: 1, units: 1, has_timeout: 1; 139 | } wait; 140 | 141 | struct { 142 | uintptr_t func; 143 | uint64_t id; 144 | } wlab; 145 | 146 | struct { 147 | struct codegen_opd cond; 148 | uint64_t loc; 149 | } jmp; 150 | 151 | struct { 152 | struct codegen_opd val, ssp; 153 | } push; 154 | 155 | struct { 156 | struct codegen_opd val, loc, bp; 157 | } call; 158 | 159 | struct { 160 | struct codegen_opd addend, tsize; 161 | } incsp; 162 | 163 | struct { 164 | struct codegen_opd val, size; 165 | } ret; 166 | }; 167 | }; 168 | 169 | struct codegen_datum { 170 | const char *name; 171 | const struct type *type; 172 | size_t off, size; 173 | }; 174 | 175 | struct codegen_subr { 176 | const char *name; 177 | size_t param_count; 178 | const struct type **param_types; 179 | const struct type *rettype; 180 | size_t off, size; 181 | }; 182 | 183 | struct codegen_type { 184 | const char *name; 185 | const struct type *type; 186 | }; 187 | 188 | struct codegen_obj { 189 | struct codegen_subr *exposed_subrs; 190 | struct codegen_datum *exposed_data; 191 | struct codegen_type *exposed_types; 192 | 193 | size_t data_size; 194 | size_t insn_count; 195 | 196 | struct { 197 | uint8_t *mem; 198 | size_t size; 199 | } strings; 200 | 201 | struct codegen_insn *insns; 202 | }; 203 | 204 | int codegen_obj_create(const struct ast_node *, struct codegen_obj *); 205 | void codegen_obj_destroy(const struct codegen_obj *); 206 | 207 | enum { 208 | CODEGEN_OK = 0, 209 | CODEGEN_NOMEM, 210 | CODEGEN_UNRESOLVED, 211 | }; 212 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ALIGN_UP(addr, align) { \ 4 | const __typeof__(addr) remainder = (addr) % (align); \ 5 | (addr) += remainder ? (align) - remainder : 0; \ 6 | } 7 | 8 | #define powerof2(x) ({ \ 9 | const __typeof__(x) x_once = (x); \ 10 | x_once && (x_once & (x_once - 1)) == 0; \ 11 | }) 12 | 13 | #define countof(arr) (sizeof(arr) / sizeof(*(arr))) 14 | #define likely(exp) __builtin_expect(!!(exp), 1) 15 | #define unlikely(exp) __builtin_expect(!!(exp), 0) 16 | 17 | #ifdef COLOURLESS 18 | #define COLOURED(s, b, c) s 19 | #else 20 | #define COLOURED(s, b, c) "\033[" #b ";" #c "m" s "\033[0m" 21 | #endif 22 | 23 | #define GRAY(s) COLOURED(s, 0, 37) 24 | #define RED(s) COLOURED(s, 1, 31) 25 | #define GREEN(s) COLOURED(s, 1, 32) 26 | #define YELLOW(s) COLOURED(s, 1, 33) 27 | #define ORANGE(s) COLOURED(s, 1, 34) 28 | #define CYAN(s) COLOURED(s, 1, 36) 29 | #define WHITE(s) COLOURED(s, 1, 37) 30 | -------------------------------------------------------------------------------- /src/exec.c: -------------------------------------------------------------------------------- 1 | #include "exec.h" 2 | 3 | #include "codegen.h" 4 | #include "scope.h" /* we only need built-in function id's */ 5 | 6 | #include "common.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /* OS X doesn't have clock_gettime(), define a drop-in replacement */ 18 | #if defined(__MACH__) && !defined(CLOCK_MONOTONIC) 19 | #include 20 | #include 21 | 22 | #define CLOCK_MONOTONIC SYSTEM_CLOCK 23 | 24 | static int clock_gettime(const clock_id_t clk_id, struct timespec *const ts) 25 | { 26 | clock_serv_t cclock; 27 | 28 | if (unlikely(host_get_clock_service(mach_host_self(), clk_id, &cclock))) { 29 | return errno = EINVAL; 30 | } 31 | 32 | mach_timespec_t mts; 33 | 34 | if (unlikely(clock_get_time(cclock, &mts))) { 35 | mach_port_deallocate(mach_task_self(), cclock); 36 | return errno = EINVAL; 37 | } 38 | 39 | mach_port_deallocate(mach_task_self(), cclock); 40 | 41 | ts->tv_sec = (__darwin_time_t) mts.tv_sec; 42 | ts->tv_nsec = mts.tv_nsec; 43 | 44 | return 0; 45 | } 46 | 47 | #endif 48 | 49 | static int get_monotonic_time(uint64_t *const value) 50 | { 51 | struct timespec ts; 52 | 53 | if (clock_gettime(CLOCK_MONOTONIC, &ts)) { 54 | return perror("clock_gettime"), -1; 55 | } 56 | 57 | *value = (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; 58 | return 0; 59 | } 60 | 61 | #define LEGAL_IF(cond, msg, ...) \ 62 | if (unlikely(!(cond))) { \ 63 | fprintf(stderr, "%s:%d: illegal instruction at %" PRIu64 ": ", \ 64 | __FILE__, __LINE__, vm->ip); \ 65 | \ 66 | fputs(#cond, stderr); \ 67 | fprintf(stderr, ": " msg "\n", ## __VA_ARGS__); \ 68 | return EXEC_ILLEGAL; \ 69 | } 70 | 71 | static uint64_t now; 72 | static uint8_t *bss; 73 | static uint64_t bss_size; 74 | 75 | struct tmp_frame { 76 | struct tmp_frame *prev; 77 | uint8_t mem[]; 78 | }; 79 | 80 | #define STACK_SIZE (4096 * 4 - sizeof(struct qvm)) 81 | 82 | struct qvm { 83 | struct qvm *parent; 84 | uint64_t ip, sp, bp; 85 | uint64_t at_start: 1, at_end: 1, noint: 1, 86 | waiting: 1, waiting_for: 1, waiting_until: 1, waiting_noblock: 1; 87 | 88 | union { 89 | struct { 90 | uint64_t start, interval; 91 | } wait_for; 92 | 93 | struct { 94 | uintptr_t func; 95 | uint64_t id; 96 | } wait_until; 97 | }; 98 | 99 | struct { 100 | uintptr_t func; 101 | uint64_t id; 102 | } last_passed; 103 | 104 | struct tmp_frame *temps; 105 | uint8_t stack[] __attribute__((aligned(8))); 106 | }; 107 | 108 | static struct qvm *vm; 109 | static const struct codegen_obj *o; 110 | 111 | static uint64_t opd_size(const struct codegen_opd *const operand) 112 | { 113 | switch (operand->opd) { 114 | case CODEGEN_OPD_IMM: 115 | assert(operand->signd == 0); 116 | assert(operand->indirect == 0); 117 | assert(powerof2(operand->immsize) && operand->immsize <= 8); 118 | return operand->immsize; 119 | 120 | case CODEGEN_OPD_TEMP: 121 | case CODEGEN_OPD_AUTO: 122 | case CODEGEN_OPD_GLOB: 123 | assert(operand->size != 0); 124 | return operand->size; 125 | 126 | default: assert(0), abort(); 127 | } 128 | } 129 | 130 | static void *opd_val(const struct codegen_opd *const operand) 131 | { 132 | void *base; 133 | 134 | switch (operand->opd) { 135 | case CODEGEN_OPD_IMM: 136 | return (void *) &operand->imm; 137 | 138 | case CODEGEN_OPD_TEMP: { 139 | assert(vm->temps != NULL); 140 | assert(vm->temps->mem != NULL); 141 | base = vm->temps->mem + operand->off; 142 | } break; 143 | 144 | case CODEGEN_OPD_AUTO: { 145 | assert(vm->bp + operand->off < STACK_SIZE); 146 | base = vm->stack + vm->bp + operand->off; 147 | } break; 148 | 149 | case CODEGEN_OPD_GLOB: { 150 | assert(operand->off + (operand->indirect ? 8 : operand->size) <= bss_size); 151 | base = bss + operand->off; 152 | } break; 153 | 154 | default: assert(0), abort(); 155 | } 156 | 157 | if (operand->indirect) { 158 | const uint64_t ptr = *(uint64_t *) base; 159 | 160 | if (ptr == 0) { 161 | fprintf(stderr, "warn: null pointer dereference\n"); 162 | } 163 | 164 | return (void *) (uintptr_t) ptr; 165 | } else { 166 | return base; 167 | } 168 | } 169 | 170 | static int exit_status_from_retval(const struct codegen_insn *const insn) 171 | { 172 | const uint8_t val_signd = insn->ret.val.signd; 173 | const uint64_t val_size = opd_size(&insn->ret.val); 174 | const void *const val = opd_val(&insn->ret.val); 175 | 176 | switch (val_size) { 177 | case 1: return val_signd ? (int) *( int8_t *) val : (int) *( uint8_t *) val; 178 | case 2: return val_signd ? (int) *(int16_t *) val : (int) *(uint16_t *) val; 179 | case 4: return val_signd ? (int) *(int32_t *) val : (int) *(uint32_t *) val; 180 | case 8: return val_signd ? (int) *(int64_t *) val : (int) *(uint64_t *) val; 181 | } 182 | 183 | return 0; 184 | } 185 | 186 | static void check_and_eventually_split_vms(void) 187 | { 188 | static uint64_t cycles; 189 | 190 | if (cycles++ % 200 == 0 && get_monotonic_time(&now)) { 191 | return; 192 | } 193 | 194 | uint8_t noint = vm->noint; 195 | struct qvm *old_vm = vm, *current_vm = vm->parent; 196 | 197 | while (current_vm && !noint) { 198 | bool split = false; 199 | 200 | if (current_vm->waiting) { 201 | if (current_vm->waiting_for) { 202 | split = now - current_vm->wait_for.start >= current_vm->wait_for.interval; 203 | } else if (current_vm->waiting_until) { 204 | split = old_vm->last_passed.func == current_vm->wait_until.func && 205 | old_vm->last_passed.id == current_vm->wait_until.id; 206 | } 207 | } 208 | 209 | if (split) { 210 | vm = current_vm; 211 | vm->waiting = 0; 212 | vm->waiting_for = 0; 213 | vm->waiting_until = 0; 214 | vm->waiting_noblock = 0; 215 | vm->ip++; 216 | return; 217 | } 218 | 219 | noint = current_vm->noint; 220 | old_vm = current_vm; 221 | current_vm = current_vm->parent; 222 | } 223 | } 224 | 225 | static void cleanup_temps(void **const temps) 226 | { 227 | free(*temps); 228 | } 229 | 230 | static int handle_return(const struct codegen_insn *insn, 231 | const uint64_t retval_size, const void *const retval) 232 | { 233 | void *old_temps __attribute__((__cleanup__(cleanup_temps))) = vm->temps; 234 | (void) old_temps; 235 | 236 | if (vm->sp == 0 && vm->parent) { 237 | insn = &o->insns[vm->parent->ip]; 238 | 239 | LEGAL_IF(insn->op == (retval_size ? CODEGEN_OP_RTEV : CODEGEN_OP_RTE) || 240 | insn->op == CODEGEN_OP_WAIT, ""); 241 | 242 | switch (insn->op) { 243 | case CODEGEN_OP_RTEV: { 244 | struct qvm *const old_vm = vm; 245 | vm = vm->parent; 246 | const uint64_t cval_size = opd_size(&insn->un.dst); 247 | void *const cval = opd_val(&insn->un.dst); 248 | 249 | LEGAL_IF(retval_size == cval_size, "%" PRIu64 ", %" PRIu64, 250 | retval_size, cval_size); 251 | 252 | memcpy(cval, retval, (size_t) cval_size); 253 | free(old_vm); 254 | *(uint64_t *) opd_val(&insn->un.src) = 0; 255 | } break; 256 | 257 | case CODEGEN_OP_RTE: { 258 | struct qvm *const old_vm = vm; 259 | vm = vm->parent; 260 | free(old_vm); 261 | *(uint64_t *) opd_val(&insn->un.src) = 0; 262 | } break; 263 | 264 | case CODEGEN_OP_WAIT: { 265 | vm->at_end = 1; 266 | vm->last_passed.func = 0; 267 | vm->last_passed.id = 0; 268 | 269 | if (retval_size) { 270 | memcpy(vm->stack, retval, (size_t) retval_size); 271 | } 272 | 273 | vm->temps = NULL; 274 | vm = vm->parent; 275 | vm->waiting = 0; 276 | vm->waiting_for = 0; 277 | vm->waiting_until = 0; 278 | vm->waiting_noblock = 0; 279 | } break; 280 | } 281 | 282 | vm->ip++; 283 | } else if (vm->sp == 0) { 284 | const int status = retval_size ? exit_status_from_retval(insn) : 0; 285 | vm->temps = NULL; 286 | vm->ip = *(uint64_t *) (vm->stack + vm->sp); 287 | vm->bp = *(uint64_t *) (vm->stack + vm->sp + 8); 288 | return status; 289 | } else { 290 | vm->temps = vm->temps->prev; 291 | vm->ip = *(uint64_t *) (vm->stack + vm->sp); 292 | vm->bp = *(uint64_t *) (vm->stack + vm->sp + 8); 293 | 294 | LEGAL_IF(vm->ip < o->insn_count, "%" PRIu64, vm->ip); 295 | LEGAL_IF(vm->bp <= STACK_SIZE, "%" PRIu64, vm->bp); 296 | 297 | if (retval_size) { 298 | insn = &o->insns[vm->ip]; 299 | LEGAL_IF(insn->op == CODEGEN_OP_CALLV, ""); 300 | const uint64_t cval_size = opd_size(&insn->call.val); 301 | 302 | LEGAL_IF(retval_size == cval_size, "%" PRIu64 ", %" PRIu64, 303 | retval_size, cval_size); 304 | 305 | void *const cval = opd_val(&insn->call.val); 306 | memcpy(cval, retval, (size_t) cval_size); 307 | } 308 | 309 | vm->ip++; 310 | } 311 | 312 | return EXEC_OK; 313 | } 314 | 315 | static int insn_mov(const struct codegen_insn *const insn) 316 | { 317 | assert(insn->op == CODEGEN_OP_MOV); 318 | 319 | const uint64_t dst_size = opd_size(&insn->un.dst); 320 | const uint64_t src_size = opd_size(&insn->un.src); 321 | 322 | LEGAL_IF(dst_size == src_size, "%" PRIu64 ", %" PRIu64, dst_size, src_size); 323 | 324 | void *const dst = opd_val(&insn->un.dst); 325 | const void *const src = opd_val(&insn->un.src); 326 | 327 | memcpy(dst, src, (size_t) dst_size); 328 | return EXEC_OK; 329 | } 330 | 331 | static int insn_cast(const struct codegen_insn *const insn) 332 | { 333 | assert(insn->op == CODEGEN_OP_CAST); 334 | 335 | const uint64_t dst_size = opd_size(&insn->un.dst); 336 | const uint64_t src_size = opd_size(&insn->un.src); 337 | 338 | void *const dst = opd_val(&insn->un.dst); 339 | const void *const src = opd_val(&insn->un.src); 340 | 341 | memset(dst, 0, (size_t) dst_size); 342 | memcpy(dst, src, src_size < dst_size ? (size_t) src_size : (size_t) dst_size); 343 | return EXEC_OK; 344 | } 345 | 346 | static int insn_bin_arith(const struct codegen_insn *const insn) 347 | { 348 | LEGAL_IF(insn->bin.dst.signd == insn->bin.src1.signd && 349 | insn->bin.src1.signd == insn->bin.src2.signd, "differing signedness"); 350 | 351 | const uint8_t signd = insn->bin.dst.signd; 352 | const uint64_t dst_size = opd_size(&insn->bin.dst); 353 | const uint64_t src1_size = opd_size(&insn->bin.src1); 354 | const uint64_t src2_size = opd_size(&insn->bin.src2); 355 | 356 | LEGAL_IF(powerof2(dst_size) && dst_size <= 8, "%" PRIu64, dst_size); 357 | LEGAL_IF(powerof2(src1_size) && src1_size <= 8, "%" PRIu64, src1_size); 358 | LEGAL_IF(powerof2(src2_size) && src2_size <= 8, "%" PRIu64, src2_size); 359 | LEGAL_IF(dst_size == src1_size && src1_size == src2_size, "differing sizes"); 360 | 361 | void *const dst = opd_val(&insn->bin.dst); 362 | const void *const src1 = opd_val(&insn->bin.src1); 363 | const void *const src2 = opd_val(&insn->bin.src2); 364 | 365 | #define ARITH_OP(op) \ 366 | switch (dst_size) { \ 367 | case 1: \ 368 | if (signd) { \ 369 | *(int8_t *) dst = (int8_t) (*(int8_t *) src1 op *(int8_t *) src2); \ 370 | } else { \ 371 | *(uint8_t *) dst = (uint8_t) (*(uint8_t *) src1 op *(uint8_t *) src2); \ 372 | } \ 373 | break; \ 374 | \ 375 | case 2: \ 376 | if (signd) { \ 377 | *(int16_t *) dst = (int16_t) (*(int16_t *) src1 op *(int16_t *) src2); \ 378 | } else { \ 379 | *(uint16_t *) dst = (uint16_t) (*(uint16_t *) src1 op *(uint16_t *) src2); \ 380 | } \ 381 | break; \ 382 | \ 383 | case 4: \ 384 | if (signd) { \ 385 | *(int32_t *) dst = *(int32_t *) src1 op *(int32_t *) src2; \ 386 | } else { \ 387 | *(uint32_t *) dst = *(uint32_t *) src1 op *(uint32_t *) src2; \ 388 | } \ 389 | break; \ 390 | \ 391 | case 8: \ 392 | if (signd) { \ 393 | *(int64_t *) dst = *(int64_t *) src1 op *(int64_t *) src2; \ 394 | } else { \ 395 | *(uint64_t *) dst = *(uint64_t *) src1 op *(uint64_t *) src2; \ 396 | } \ 397 | break; \ 398 | } 399 | 400 | switch (insn->op) { 401 | case CODEGEN_OP_ADD: ARITH_OP( +); break; 402 | case CODEGEN_OP_SUB: ARITH_OP( -); break; 403 | case CODEGEN_OP_MUL: ARITH_OP( *); break; 404 | case CODEGEN_OP_DIV: ARITH_OP( /); break; 405 | case CODEGEN_OP_MOD: ARITH_OP( %); break; 406 | case CODEGEN_OP_LSH: ARITH_OP(<<); break; 407 | case CODEGEN_OP_RSH: ARITH_OP(>>); break; 408 | case CODEGEN_OP_AND: ARITH_OP( &); break; 409 | case CODEGEN_OP_XOR: ARITH_OP( ^); break; 410 | case CODEGEN_OP_OR: ARITH_OP( |); break; 411 | default: assert(0), abort(); 412 | } 413 | 414 | #undef ARITH_OP 415 | return EXEC_OK; 416 | } 417 | 418 | static int insn_equ_neq(const struct codegen_insn *const insn) 419 | { 420 | assert(insn->op == CODEGEN_OP_EQU || insn->op == CODEGEN_OP_NEQ); 421 | 422 | const uint8_t dst_signd = insn->bin.dst.signd; 423 | const uint64_t dst_size = opd_size(&insn->bin.dst); 424 | const uint64_t src1_size = opd_size(&insn->bin.src1); 425 | const uint64_t src2_size = opd_size(&insn->bin.src2); 426 | 427 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 428 | LEGAL_IF(dst_size == 1, "%" PRIu64, dst_size); 429 | LEGAL_IF(src1_size == src2_size, "%" PRIu64 ", %" PRIu64, src1_size, src2_size); 430 | 431 | uint8_t *const dst = opd_val(&insn->bin.dst); 432 | const void *const src1 = opd_val(&insn->bin.src1); 433 | const void *const src2 = opd_val(&insn->bin.src2); 434 | 435 | const int equal = !memcmp(src1, src2, (size_t) src1_size); 436 | *dst = insn->op == CODEGEN_OP_EQU ? (equal ? 1 : 0) : !equal; 437 | return EXEC_OK; 438 | } 439 | 440 | static int insn_bin_logic(const struct codegen_insn *const insn) 441 | { 442 | assert(insn->op == CODEGEN_OP_LT || insn->op == CODEGEN_OP_GT || 443 | insn->op == CODEGEN_OP_LTE || insn->op == CODEGEN_OP_GTE); 444 | 445 | const uint8_t dst_signd = insn->bin.dst.signd; 446 | const uint64_t dst_size = opd_size(&insn->bin.dst); 447 | 448 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 449 | LEGAL_IF(dst_size == 1, "%" PRIu64, dst_size); 450 | 451 | const uint8_t src1_signd = insn->bin.src1.signd; 452 | const uint64_t src1_size = opd_size(&insn->bin.src1); 453 | const uint8_t src2_signd = insn->bin.src2.signd; 454 | const uint64_t src2_size = opd_size(&insn->bin.src2); 455 | 456 | LEGAL_IF(src1_signd == src2_signd, "differing signedness"); 457 | LEGAL_IF(powerof2(src1_size) && src1_size <= 8, "%" PRIu64, src1_size); 458 | LEGAL_IF(powerof2(src2_size) && src2_size <= 8, "%" PRIu64, src2_size); 459 | LEGAL_IF(src1_size == src2_size, "differing sizes"); 460 | 461 | uint8_t *const dst = opd_val(&insn->bin.dst); 462 | const void *const src1 = opd_val(&insn->bin.src1); 463 | const void *const src2 = opd_val(&insn->bin.src2); 464 | 465 | #define LOGIC_OP(op) \ 466 | switch (src1_size) { \ 467 | case 1: \ 468 | if (src1_signd) { \ 469 | *dst = *(int8_t *) src1 op *(int8_t *) src2; \ 470 | } else { \ 471 | *dst = *(uint8_t *) src1 op *(uint8_t *) src2; \ 472 | } \ 473 | break; \ 474 | \ 475 | case 2: \ 476 | if (src1_signd) { \ 477 | *dst = *(int16_t *) src1 op *(int16_t *) src2; \ 478 | } else { \ 479 | *dst = *(uint16_t *) src1 op *(uint16_t *) src2; \ 480 | } \ 481 | break; \ 482 | \ 483 | case 4: \ 484 | if (src1_signd) { \ 485 | *dst = *(int32_t *) src1 op *(int32_t *) src2; \ 486 | } else { \ 487 | *dst = *(uint32_t *) src1 op *(uint32_t *) src2; \ 488 | } \ 489 | break; \ 490 | \ 491 | case 8: \ 492 | if (src1_signd) { \ 493 | *dst = *(int64_t *) src1 op *(int64_t *) src2; \ 494 | } else { \ 495 | *dst = *(uint64_t *) src1 op *(uint64_t *) src2; \ 496 | } \ 497 | break; \ 498 | } 499 | 500 | switch (insn->op) { 501 | case CODEGEN_OP_LT: LOGIC_OP( <); break; 502 | case CODEGEN_OP_GT: LOGIC_OP( >); break; 503 | case CODEGEN_OP_LTE: LOGIC_OP(<=); break; 504 | case CODEGEN_OP_GTE: LOGIC_OP(>=); break; 505 | default: assert(0), abort(); 506 | } 507 | 508 | #undef LOGIC_OP 509 | return EXEC_OK; 510 | } 511 | 512 | static int insn_not(const struct codegen_insn *const insn) 513 | { 514 | assert(insn->op == CODEGEN_OP_NOT); 515 | 516 | const uint8_t dst_signd = insn->un.dst.signd; 517 | const uint64_t dst_size = opd_size(&insn->un.dst); 518 | 519 | LEGAL_IF(dst_signd == 0 || dst_signd == 1, "%u", dst_signd); 520 | LEGAL_IF(powerof2(dst_size) && dst_size <= 8, "%" PRIu64, dst_size); 521 | 522 | const uint8_t src_signd = insn->un.src.signd; 523 | const uint64_t src_size = opd_size(&insn->un.src); 524 | 525 | LEGAL_IF(src_signd == dst_signd, "%u, %u", src_signd, dst_signd); 526 | LEGAL_IF(powerof2(src_size) && src_size <= 8, "%" PRIu64, src_size); 527 | LEGAL_IF(dst_size == src_size, "%" PRIu64 ", %" PRIu64, dst_size, src_size); 528 | 529 | void *const dst = opd_val(&insn->un.dst); 530 | const void *const src = opd_val(&insn->un.src); 531 | 532 | switch (dst_size) { 533 | case 1: 534 | if (dst_signd) { 535 | *(int8_t *) dst = !*(int8_t *) src; 536 | } else { 537 | *(uint8_t *) dst = !*(uint8_t *) src; 538 | } 539 | break; 540 | 541 | case 2: 542 | if (dst_signd) { 543 | *(int16_t *) dst = !*(int16_t *) src; 544 | } else { 545 | *(uint16_t *) dst = !*(uint16_t *) src; 546 | } 547 | break; 548 | 549 | case 4: 550 | if (dst_signd) { 551 | *(int32_t *) dst = !*(int32_t *) src; 552 | } else { 553 | *(uint32_t *) dst = !*(uint32_t *) src; 554 | } 555 | break; 556 | 557 | case 8: 558 | if (dst_signd) { 559 | *(int64_t *) dst = !*(int64_t *) src; 560 | } else { 561 | *(uint64_t *) dst = !*(uint64_t *) src; 562 | } 563 | break; 564 | } 565 | 566 | return EXEC_OK; 567 | } 568 | 569 | static int insn_neg(const struct codegen_insn *const insn) 570 | { 571 | assert(insn->op == CODEGEN_OP_NEG); 572 | 573 | const uint8_t dst_signd = insn->un.dst.signd; 574 | const uint64_t dst_size = opd_size(&insn->un.dst); 575 | 576 | LEGAL_IF(dst_signd == 1, "%u", dst_signd); 577 | LEGAL_IF(powerof2(dst_size) && dst_size <= 8, "%" PRIu64, dst_size); 578 | 579 | const uint8_t src_signd = insn->un.src.signd; 580 | const uint64_t src_size = opd_size(&insn->un.src); 581 | 582 | LEGAL_IF(src_signd == 0 || src_signd == 1, "%u", src_signd); 583 | LEGAL_IF(powerof2(src_size) && src_size <= 8, "%" PRIu64, src_size); 584 | LEGAL_IF(dst_size == src_size, "%" PRIu64 ", %" PRIu64, dst_size, src_size); 585 | 586 | void *const dst = opd_val(&insn->un.dst); 587 | const void *const src = opd_val(&insn->un.src); 588 | 589 | switch (dst_size) { 590 | case 1: 591 | if (src_signd) { 592 | *(int8_t *) dst = (int8_t) -*(int8_t *) src; 593 | } else { 594 | *(int8_t *) dst = (int8_t) -*(uint8_t *) src; 595 | } 596 | break; 597 | 598 | case 2: 599 | if (src_signd) { 600 | *(int16_t *) dst = (int16_t) -*(int16_t *) src; 601 | } else { 602 | *(int16_t *) dst = (int16_t) -*(uint16_t *) src; 603 | } 604 | break; 605 | 606 | case 4: 607 | if (src_signd) { 608 | *(int32_t *) dst = -*(int32_t *) src; 609 | } else { 610 | *(int32_t *) dst = (int32_t) -*(uint32_t *) src; 611 | } 612 | break; 613 | 614 | case 8: 615 | if (src_signd) { 616 | *(int64_t *) dst = -*(int64_t *) src; 617 | } else { 618 | *(int64_t *) dst = (int64_t) -*(uint64_t *) src; 619 | } 620 | break; 621 | } 622 | 623 | return EXEC_OK; 624 | } 625 | 626 | static int insn_bneg(const struct codegen_insn *const insn) 627 | { 628 | assert(insn->op == CODEGEN_OP_BNEG); 629 | 630 | const uint8_t dst_signd = insn->un.dst.signd; 631 | const uint64_t dst_size = opd_size(&insn->un.dst); 632 | 633 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 634 | LEGAL_IF(powerof2(dst_size) && dst_size <= 8, "%" PRIu64, dst_size); 635 | 636 | const uint8_t src_signd = insn->un.src.signd; 637 | const uint64_t src_size = opd_size(&insn->un.src); 638 | 639 | LEGAL_IF(src_signd == 0, "%u", src_signd); 640 | LEGAL_IF(powerof2(src_size) && src_size <= 8, "%" PRIu64, src_size); 641 | LEGAL_IF(dst_size == src_size, "%" PRIu64 ", %" PRIu64, dst_size, src_size); 642 | 643 | void *const dst = opd_val(&insn->un.dst); 644 | const void *const src = opd_val(&insn->un.src); 645 | 646 | switch (dst_size) { 647 | case 1: 648 | *(uint8_t *) dst = ~*(uint8_t *) src; 649 | break; 650 | 651 | case 2: 652 | *(uint16_t *) dst = ~*(uint16_t *) src; 653 | break; 654 | 655 | case 4: 656 | *(uint32_t *) dst = ~*(uint32_t *) src; 657 | break; 658 | 659 | case 8: 660 | *(uint64_t *) dst = ~*(uint64_t *) src; 661 | break; 662 | } 663 | 664 | return EXEC_OK; 665 | } 666 | 667 | static int insn_oz(const struct codegen_insn *const insn) 668 | { 669 | assert(insn->op == CODEGEN_OP_OZ); 670 | 671 | const uint8_t dst_signd = insn->un.dst.signd; 672 | const uint64_t dst_size = opd_size(&insn->un.dst); 673 | 674 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 675 | LEGAL_IF(dst_size == 1, "%" PRIu64, dst_size); 676 | 677 | const uint8_t src_signd = insn->un.src.signd; 678 | const uint64_t src_size = opd_size(&insn->un.src); 679 | 680 | LEGAL_IF(src_signd == 0 || src_signd == 1, "%u", src_signd); 681 | LEGAL_IF(powerof2(src_size) && src_size <= 8, "%" PRIu64, src_size); 682 | 683 | uint8_t *const dst = opd_val(&insn->un.dst); 684 | const void *const src = opd_val(&insn->un.src); 685 | 686 | switch (src_size) { 687 | case 1: 688 | *dst = src_signd ? (*(int8_t *) src ? 1 : 0) : (*(uint8_t *) src ? 1 : 0); 689 | break; 690 | 691 | case 2: 692 | *dst = src_signd ? (*(int16_t *) src ? 1 : 0) : (*(uint16_t *) src ? 1 : 0); 693 | break; 694 | 695 | case 4: 696 | *dst = src_signd ? (*(int32_t *) src ? 1 : 0) : (*(uint32_t *) src ? 1 : 0); 697 | break; 698 | 699 | case 8: 700 | *dst = src_signd ? (*(int64_t *) src ? 1 : 0) : (*(uint64_t *) src ? 1 : 0); 701 | break; 702 | } 703 | 704 | return EXEC_OK; 705 | } 706 | 707 | static int insn_inc_dec(const struct codegen_insn *const insn) 708 | { 709 | assert(insn->op == CODEGEN_OP_INC || insn->op == CODEGEN_OP_DEC); 710 | 711 | const uint8_t dst_signd = insn->dst.signd; 712 | const uint64_t dst_size = opd_size(&insn->dst); 713 | 714 | LEGAL_IF(dst_signd == 0 || dst_signd == 1, "%u", dst_signd); 715 | LEGAL_IF(powerof2(dst_size) && dst_size <= 8, "%" PRIu64, dst_size); 716 | 717 | void *const dst = opd_val(&insn->dst); 718 | 719 | #define INC_DEC_OP(op) \ 720 | switch (dst_size) { \ 721 | case 1: \ 722 | if (dst_signd) { \ 723 | (*(int8_t *) dst)op; \ 724 | } else { \ 725 | (*(uint8_t *) dst)op; \ 726 | } \ 727 | break; \ 728 | \ 729 | case 2: \ 730 | if (dst_signd) { \ 731 | (*(int16_t *) dst)op; \ 732 | } else { \ 733 | (*(uint16_t *) dst)op; \ 734 | } \ 735 | break; \ 736 | \ 737 | case 4: \ 738 | if (dst_signd) { \ 739 | (*(int32_t *) dst)op; \ 740 | } else { \ 741 | (*(uint32_t *) dst)op; \ 742 | } \ 743 | break; \ 744 | \ 745 | case 8: \ 746 | if (dst_signd) { \ 747 | (*(int64_t *) dst)op; \ 748 | } else { \ 749 | (*(uint64_t *) dst)op; \ 750 | } \ 751 | break; \ 752 | } 753 | 754 | if (insn->op == CODEGEN_OP_INC) { 755 | INC_DEC_OP(++); 756 | } else { 757 | INC_DEC_OP(--); 758 | } 759 | 760 | #undef INC_DEC_OP 761 | return EXEC_OK; 762 | } 763 | 764 | static int insn_incp_decp(const struct codegen_insn *const insn) 765 | { 766 | assert(insn->op == CODEGEN_OP_INCP || insn->op == CODEGEN_OP_DECP); 767 | 768 | const uint8_t dst_signd = insn->un.dst.signd; 769 | const uint64_t dst_size = opd_size(&insn->un.dst); 770 | 771 | LEGAL_IF(dst_signd == 0 || dst_signd == 1, "%u", dst_signd); 772 | LEGAL_IF(powerof2(dst_size) && dst_size <= 8, "%" PRIu64, dst_size); 773 | 774 | const uint8_t src_signd = insn->un.src.signd; 775 | const uint64_t src_size = opd_size(&insn->un.src); 776 | 777 | LEGAL_IF(src_signd == dst_signd, "%u, %u", src_signd, dst_signd); 778 | LEGAL_IF(powerof2(src_size) && src_size <= 8, "%" PRIu64, src_size); 779 | LEGAL_IF(dst_size == src_size, "%" PRIu64 ", %" PRIu64, dst_size, src_size); 780 | 781 | void *const dst = opd_val(&insn->un.dst); 782 | void *const src = opd_val(&insn->un.src); 783 | 784 | #define INCP_DECP_OP(op) \ 785 | switch (dst_size) { \ 786 | case 1: \ 787 | if (dst_signd) { \ 788 | *(int8_t *) dst = (*(int8_t *) src)op; \ 789 | } else { \ 790 | *(uint8_t *) dst = (*(uint8_t *) src)op; \ 791 | } \ 792 | break; \ 793 | \ 794 | case 2: \ 795 | if (dst_signd) { \ 796 | *(int16_t *) dst = (*(int16_t *) src)op; \ 797 | } else { \ 798 | *(uint16_t *) dst = (*(uint16_t *) src)op; \ 799 | } \ 800 | break; \ 801 | \ 802 | case 4: \ 803 | if (dst_signd) { \ 804 | *(int32_t *) dst = (*(int32_t *) src)op; \ 805 | } else { \ 806 | *(uint32_t *) dst = (*(uint32_t *) src)op; \ 807 | } \ 808 | break; \ 809 | \ 810 | case 8: \ 811 | if (dst_signd) { \ 812 | *(int64_t *) dst = (*(int64_t *) src)op; \ 813 | } else { \ 814 | *(uint64_t *) dst = (*(uint64_t *) src)op; \ 815 | } \ 816 | break; \ 817 | } 818 | 819 | if (insn->op == CODEGEN_OP_INCP) { 820 | INCP_DECP_OP(++); 821 | } else { 822 | INCP_DECP_OP(--); 823 | } 824 | 825 | #undef INCP_DECP_OP 826 | return EXEC_OK; 827 | } 828 | 829 | static int insn_cjmp(const struct codegen_insn *const insn) 830 | { 831 | assert(insn->op == CODEGEN_OP_JZ || insn->op == CODEGEN_OP_JNZ); 832 | 833 | const uint8_t cond_signd = insn->jmp.cond.signd; 834 | const uint64_t cond_size = opd_size(&insn->jmp.cond); 835 | 836 | LEGAL_IF(cond_signd == 0 || cond_signd == 1, "%u", cond_signd); 837 | LEGAL_IF(cond_size > 0, "0"); 838 | 839 | const void *const cond = opd_val(&insn->jmp.cond); 840 | static const uint8_t zero_mem[128]; 841 | int all_zero; 842 | 843 | if (unlikely(cond_size > 128)) { 844 | void *const zero_alloc = calloc(1, (size_t) cond_size); 845 | 846 | if (unlikely(!zero_alloc)) { 847 | return EXEC_NOMEM; 848 | } 849 | 850 | all_zero = !memcmp(zero_alloc, cond, (size_t) cond_size); 851 | free(zero_alloc); 852 | } else { 853 | all_zero = !memcmp(zero_mem, cond, (size_t) cond_size); 854 | } 855 | 856 | if (insn->op == CODEGEN_OP_JZ) { 857 | if (all_zero) { 858 | vm->ip = insn->jmp.loc; 859 | } else { 860 | vm->ip++; 861 | } 862 | } else { 863 | if (all_zero) { 864 | vm->ip++; 865 | } else { 866 | vm->ip = insn->jmp.loc; 867 | } 868 | } 869 | 870 | return EXEC_OK; 871 | } 872 | 873 | static int insn_jmp(const struct codegen_insn *const insn) 874 | { 875 | assert(insn->op == CODEGEN_OP_JMP); 876 | vm->ip = insn->jmp.loc; 877 | return EXEC_OK; 878 | } 879 | 880 | static int insn_pushr(const struct codegen_insn *const insn) 881 | { 882 | assert(insn->op == CODEGEN_OP_PUSHR); 883 | 884 | const uint8_t retip_is_imm = insn->push.val.opd == CODEGEN_OPD_IMM; 885 | const uint8_t retip_signd = insn->push.val.signd; 886 | const uint8_t retip_indirect = insn->push.val.indirect; 887 | const uint64_t retip_size = opd_size(&insn->push.val); 888 | 889 | LEGAL_IF(retip_is_imm == 1, "%u", retip_is_imm); 890 | LEGAL_IF(retip_signd == 0, "%u", retip_signd); 891 | LEGAL_IF(retip_indirect == 0, "%u", retip_indirect); 892 | LEGAL_IF(retip_size == 8, "%" PRIu64, retip_size); 893 | 894 | const uint8_t ssp_is_temp = insn->push.ssp.opd == CODEGEN_OPD_TEMP; 895 | const uint8_t ssp_signd = insn->push.ssp.signd; 896 | const uint8_t ssp_indirect = insn->push.ssp.indirect; 897 | const uint64_t ssp_size = opd_size(&insn->push.ssp); 898 | 899 | LEGAL_IF(ssp_is_temp == 1, "%u", ssp_is_temp); 900 | LEGAL_IF(ssp_signd == 0, "%u", ssp_signd); 901 | LEGAL_IF(ssp_indirect == 0, "%u", ssp_indirect); 902 | LEGAL_IF(ssp_size == 8, "%" PRIu64, ssp_size); 903 | 904 | LEGAL_IF(vm->sp % 8 == 0, "%" PRIu64, vm->sp); 905 | LEGAL_IF(vm->sp + 16 <= STACK_SIZE, "%" PRIu64, vm->sp); 906 | 907 | const uint64_t retip = insn->push.val.imm; 908 | uint64_t *const ssp = opd_val(&insn->push.ssp); 909 | 910 | *(uint64_t *) (vm->stack + vm->sp) = retip; 911 | *(uint64_t *) (vm->stack + vm->sp + 8) = (uint64_t) vm->bp; 912 | 913 | vm->sp += 16; 914 | *ssp = vm->sp; 915 | 916 | return EXEC_OK; 917 | } 918 | 919 | static int insn_push(const struct codegen_insn *const insn) 920 | { 921 | assert(insn->op == CODEGEN_OP_PUSH); 922 | 923 | const uint8_t val_signd = insn->push.val.signd; 924 | const uint8_t val_indirect = insn->push.val.indirect; 925 | const uint64_t val_size = opd_size(&insn->push.val); 926 | 927 | LEGAL_IF(val_signd == 0 || val_signd == 1, "%u", val_signd); 928 | LEGAL_IF(val_indirect == 0 || val_indirect == 1, "%u", val_indirect); 929 | LEGAL_IF(val_size > 0, "%" PRIu64, val_size); 930 | 931 | LEGAL_IF(vm->sp % 8 == 0, "%" PRIu64, vm->sp); 932 | LEGAL_IF(vm->sp + val_size <= STACK_SIZE, "%" PRIu64 ", %" PRIu64, vm->sp, val_size); 933 | 934 | const void *const val = opd_val(&insn->push.val); 935 | memcpy(vm->stack + vm->sp, val, (size_t) val_size); 936 | vm->sp += val_size; 937 | ALIGN_UP(vm->sp, 8); 938 | LEGAL_IF(vm->sp <= STACK_SIZE, "%" PRIu64, vm->sp); 939 | 940 | return EXEC_OK; 941 | } 942 | 943 | static int insn_call_callv(const struct codegen_insn *const insn) 944 | { 945 | assert(insn->op == CODEGEN_OP_CALL || insn->op == CODEGEN_OP_CALLV); 946 | 947 | const uint8_t loc_signd = insn->call.loc.signd; 948 | const uint8_t loc_indirect = insn->call.loc.indirect; 949 | const uint64_t loc_size = opd_size(&insn->call.loc); 950 | 951 | LEGAL_IF(loc_signd == 0, "%u", loc_signd); 952 | LEGAL_IF(loc_indirect == 0 || loc_indirect == 1, "%u", loc_indirect); 953 | LEGAL_IF(loc_size == 8, "%" PRIu64, loc_size); 954 | 955 | const uint8_t bp_is_temp = insn->call.bp.opd == CODEGEN_OPD_TEMP; 956 | const uint8_t bp_signd = insn->call.bp.signd; 957 | const uint8_t bp_indirect = insn->call.bp.indirect; 958 | const uint64_t bp_size = opd_size(&insn->call.bp); 959 | 960 | LEGAL_IF(bp_is_temp == 1, "%u", bp_is_temp); 961 | LEGAL_IF(bp_signd == 0, "%u", bp_signd); 962 | LEGAL_IF(bp_indirect == 0, "%u", bp_indirect); 963 | LEGAL_IF(bp_size == 8, "%" PRIu64, bp_size); 964 | 965 | const uint64_t *const loc = opd_val(&insn->call.loc); 966 | const uint64_t *const bp = opd_val(&insn->call.bp); 967 | 968 | vm->ip = *loc; 969 | vm->bp = *bp; 970 | 971 | return EXEC_OK; 972 | } 973 | 974 | static int insn_incsp(const struct codegen_insn *const insn) 975 | { 976 | assert(insn->op == CODEGEN_OP_INCSP); 977 | 978 | const uint8_t addend_is_imm = insn->incsp.addend.opd == CODEGEN_OPD_IMM; 979 | const uint8_t addend_signd = insn->incsp.addend.signd; 980 | const uint8_t addend_indirect = insn->incsp.addend.indirect; 981 | const uint64_t addend_size = opd_size(&insn->incsp.addend); 982 | 983 | LEGAL_IF(addend_is_imm == 1, "%u", addend_is_imm); 984 | LEGAL_IF(addend_signd == 0, "%u", addend_signd); 985 | LEGAL_IF(addend_indirect == 0, "%u", addend_indirect); 986 | LEGAL_IF(addend_size == 8, "%" PRIu64, addend_size); 987 | 988 | const uint8_t tsize_is_imm = insn->incsp.tsize.opd == CODEGEN_OPD_IMM; 989 | const uint8_t tsize_signd = insn->incsp.tsize.signd; 990 | const uint8_t tsize_indirect = insn->incsp.tsize.indirect; 991 | const uint64_t tsize_size = opd_size(&insn->incsp.tsize); 992 | 993 | LEGAL_IF(tsize_is_imm == 1, "%u", tsize_is_imm); 994 | LEGAL_IF(tsize_signd == 0, "%u", tsize_signd); 995 | LEGAL_IF(tsize_indirect == 0, "%u", tsize_indirect); 996 | LEGAL_IF(tsize_size == 8, "%" PRIu64, tsize_size); 997 | 998 | const uint64_t addend = insn->incsp.addend.imm; 999 | const uint64_t tsize = insn->incsp.tsize.imm; 1000 | 1001 | LEGAL_IF(addend % 8 == 0, "%" PRIu64, addend); 1002 | 1003 | vm->at_start = 0; 1004 | vm->sp += addend; 1005 | LEGAL_IF(vm->sp <= STACK_SIZE, "%" PRIu64, vm->sp); 1006 | LEGAL_IF(vm->sp % 8 == 0, "%" PRIu64, vm->sp); 1007 | 1008 | struct tmp_frame *const new_tmp_frame = 1009 | malloc(sizeof(struct tmp_frame) + (size_t) tsize); 1010 | 1011 | if (unlikely(!new_tmp_frame)) { 1012 | return EXEC_NOMEM; 1013 | } 1014 | 1015 | new_tmp_frame->prev = vm->temps; 1016 | vm->temps = new_tmp_frame; 1017 | return EXEC_OK; 1018 | } 1019 | 1020 | static int insn_ret_retv(const struct codegen_insn *const insn) 1021 | { 1022 | assert(insn->op == CODEGEN_OP_RET || insn->op == CODEGEN_OP_RETV); 1023 | 1024 | LEGAL_IF(vm->sp % 8 == 0, "%" PRIu64, vm->sp); 1025 | LEGAL_IF(vm->bp % 8 == 0, "%" PRIu64, vm->bp); 1026 | 1027 | const uint8_t size_is_imm = insn->ret.size.opd == CODEGEN_OPD_IMM; 1028 | const uint8_t size_signd = insn->ret.size.signd; 1029 | const uint8_t size_indirect = insn->ret.size.indirect; 1030 | const uint64_t size_size = opd_size(&insn->ret.size); 1031 | 1032 | LEGAL_IF(size_is_imm == 1, "%u", size_is_imm); 1033 | LEGAL_IF(size_signd == 0, "%u", size_signd); 1034 | LEGAL_IF(size_indirect == 0, "%u", size_indirect); 1035 | LEGAL_IF(size_size == 8, "%" PRIu64, size_size); 1036 | 1037 | const uint64_t size = insn->ret.size.imm; 1038 | 1039 | LEGAL_IF(vm->sp >= size, "%" PRIu64 ", %" PRIu64, vm->sp, size); 1040 | LEGAL_IF(vm->temps != NULL, ""); 1041 | vm->sp -= size; 1042 | 1043 | const bool with_value = insn->op == CODEGEN_OP_RETV; 1044 | const uint64_t retval_size = with_value ? opd_size(&insn->ret.val) : 0; 1045 | const void *const retval = with_value ? opd_val(&insn->ret.val) : NULL; 1046 | 1047 | return handle_return(insn, retval_size, retval); 1048 | } 1049 | 1050 | static int insn_ref(const struct codegen_insn *const insn) 1051 | { 1052 | assert(insn->op == CODEGEN_OP_REF); 1053 | 1054 | const uint8_t dst_signd = insn->un.dst.signd; 1055 | const uint8_t dst_indirect = insn->un.dst.indirect; 1056 | const uint64_t dst_size = opd_size(&insn->un.dst); 1057 | 1058 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 1059 | LEGAL_IF(dst_indirect == 0, "%u", dst_indirect); 1060 | LEGAL_IF(dst_size == 8, "%" PRIu64, dst_size); 1061 | 1062 | uint64_t *const dst = opd_val(&insn->un.dst); 1063 | *dst = (uint64_t) (uintptr_t) opd_val(&insn->un.src); 1064 | return EXEC_OK; 1065 | } 1066 | 1067 | static int insn_drf(const struct codegen_insn *const insn) 1068 | { 1069 | assert(insn->op == CODEGEN_OP_DRF); 1070 | 1071 | const uint8_t dst_signd = insn->un.dst.signd; 1072 | const uint8_t dst_indirect = insn->un.dst.indirect; 1073 | const uint64_t dst_size = opd_size(&insn->un.dst); 1074 | 1075 | LEGAL_IF(dst_signd == 0 || dst_signd == 1, "%u", dst_signd); 1076 | LEGAL_IF(dst_indirect == 0, "%u", dst_indirect); 1077 | LEGAL_IF(dst_size > 0, "0"); 1078 | 1079 | const uint64_t src_size = opd_size(&insn->un.src); 1080 | 1081 | LEGAL_IF(src_size == 8, "%" PRIu64, src_size); 1082 | 1083 | void *const dst = opd_val(&insn->un.dst); 1084 | const uint64_t *const src = opd_val(&insn->un.src); 1085 | 1086 | memcpy(dst, (const void *) (uintptr_t) *src, (size_t) dst_size); 1087 | return EXEC_OK; 1088 | } 1089 | 1090 | static int insn_rte_rtev(const struct codegen_insn *const insn) 1091 | { 1092 | assert(insn->op == CODEGEN_OP_RTE || insn->op == CODEGEN_OP_RTEV); 1093 | const bool with_value = insn->op == CODEGEN_OP_RTEV; 1094 | 1095 | uint8_t dst_signd; 1096 | uint8_t dst_indirect; 1097 | uint64_t dst_size; 1098 | void *dst; 1099 | 1100 | if (with_value) { 1101 | dst_signd = insn->un.dst.signd; 1102 | dst_indirect = insn->un.dst.indirect; 1103 | dst_size = opd_size(&insn->un.dst); 1104 | 1105 | LEGAL_IF(dst_signd == 0 || dst_signd == 1, "%u", dst_signd); 1106 | LEGAL_IF(dst_indirect == 0, "%u", dst_indirect); 1107 | LEGAL_IF(dst_size > 0, "%" PRIu64, dst_size); 1108 | 1109 | dst = opd_val(&insn->un.dst); 1110 | } 1111 | 1112 | const uint8_t qnt_signd = insn->un.src.signd; 1113 | const uint8_t qnt_indirect = insn->un.src.indirect; 1114 | const uint64_t qnt_size = opd_size(&insn->un.src); 1115 | 1116 | LEGAL_IF(qnt_signd == 0, "%u", qnt_signd); 1117 | LEGAL_IF(qnt_indirect == 0 || qnt_indirect == 1, "%u", qnt_indirect); 1118 | LEGAL_IF(qnt_size == 8, "%" PRIu64, qnt_size); 1119 | 1120 | struct qvm *const qvm = (struct qvm *) (uintptr_t) 1121 | *(uint64_t *) opd_val(&insn->un.src); 1122 | 1123 | if (!qvm) { 1124 | if (with_value) { 1125 | memset(dst, 0, (size_t) dst_size); 1126 | } 1127 | 1128 | return vm->ip++, EXEC_OK; 1129 | } 1130 | 1131 | if (qvm->at_end) { 1132 | if (with_value) { 1133 | memcpy(dst, qvm->stack, (size_t) dst_size); 1134 | } 1135 | 1136 | free(qvm); 1137 | *(uint64_t *) opd_val(&insn->un.src) = 0; 1138 | return vm->ip++, EXEC_OK; 1139 | } 1140 | 1141 | qvm->parent = vm; 1142 | vm = qvm; 1143 | return EXEC_OK; 1144 | } 1145 | 1146 | static int insn_qat(const struct codegen_insn *const insn) 1147 | { 1148 | assert(insn->op == CODEGEN_OP_QAT); 1149 | 1150 | const uint8_t dst_signd = insn->qat.dst.signd; 1151 | const uint8_t dst_indirect = insn->qat.dst.indirect; 1152 | const uint64_t dst_size = opd_size(&insn->qat.dst); 1153 | 1154 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 1155 | LEGAL_IF(dst_indirect == 0, "%u", dst_indirect); 1156 | LEGAL_IF(dst_size == 1, "%" PRIu64, dst_size); 1157 | 1158 | const uint8_t qnt_signd = insn->qat.quaint.signd; 1159 | const uint8_t qnt_indirect = insn->qat.quaint.indirect; 1160 | const uint64_t qnt_size = opd_size(&insn->qat.quaint); 1161 | 1162 | LEGAL_IF(qnt_signd == 0, "%u", qnt_signd); 1163 | LEGAL_IF(qnt_indirect == 0 || qnt_indirect == 1, "%u", qnt_indirect); 1164 | LEGAL_IF(qnt_size == 8, "%" PRIu64, qnt_size); 1165 | 1166 | uint8_t *const dst = opd_val(&insn->qat.dst); 1167 | struct qvm *const qvm = (struct qvm *) (uintptr_t) 1168 | *(uint64_t *) opd_val(&insn->qat.quaint); 1169 | 1170 | if (!qvm) { 1171 | *dst = 0; 1172 | } else if (!insn->qat.func && !insn->qat.wlab_id) { 1173 | *dst = qvm->at_start; 1174 | } else if (insn->qat.func == 1) { 1175 | *dst = qvm->at_end; 1176 | } else { 1177 | *dst = qvm->last_passed.func == insn->qat.func && 1178 | qvm->last_passed.id == insn->qat.wlab_id; 1179 | } 1180 | 1181 | return EXEC_OK; 1182 | } 1183 | 1184 | static int insn_wait(const struct codegen_insn *const insn) 1185 | { 1186 | assert(insn->op == CODEGEN_OP_WAIT); 1187 | 1188 | const uint8_t qnt_signd = insn->wait.quaint.signd; 1189 | const uint8_t qnt_indirect = insn->wait.quaint.indirect; 1190 | const uint64_t qnt_size = opd_size(&insn->wait.quaint); 1191 | 1192 | LEGAL_IF(qnt_signd == 0, "%u", qnt_signd); 1193 | LEGAL_IF(qnt_indirect == 0 || qnt_indirect == 1, "%u", qnt_indirect); 1194 | LEGAL_IF(qnt_size == 8, "%" PRIu64, qnt_size); 1195 | 1196 | struct qvm *const qvm = (struct qvm *) (uintptr_t) 1197 | *(uint64_t *) opd_val(&insn->wait.quaint); 1198 | 1199 | if (!qvm || qvm->at_end) { 1200 | return vm->ip++, EXEC_OK; 1201 | } 1202 | 1203 | uint64_t tm_val; 1204 | 1205 | if (insn->wait.has_timeout) { 1206 | const uint8_t tm_signd = insn->wait.timeout.signd; 1207 | const uint8_t tm_indirect = insn->wait.timeout.indirect; 1208 | const uint64_t tm_size = opd_size(&insn->wait.timeout); 1209 | 1210 | LEGAL_IF(tm_signd == 0, "%u", tm_signd); 1211 | LEGAL_IF(tm_indirect == 0 || tm_indirect == 1, "%u", tm_indirect); 1212 | LEGAL_IF(powerof2(tm_size) && tm_size <= 8, "%" PRIu64, tm_size); 1213 | 1214 | const void *const tm = opd_val(&insn->wait.timeout); 1215 | 1216 | switch (tm_size) { 1217 | case 1: tm_val = *( uint8_t *) tm; break; 1218 | case 2: tm_val = *(uint16_t *) tm; break; 1219 | case 4: tm_val = *(uint32_t *) tm; break; 1220 | case 8: tm_val = *(uint64_t *) tm; break; 1221 | } 1222 | 1223 | if (!tm_val) { 1224 | return vm->ip++, EXEC_OK; 1225 | } 1226 | 1227 | tm_val *= insn->wait.units ? (uint64_t) 1000000000 : (uint64_t) 1000000; 1228 | } 1229 | 1230 | vm->waiting = 1; 1231 | vm->waiting_for = vm->waiting_until = 0; 1232 | vm->waiting_noblock = insn->wait.noblock; 1233 | 1234 | if (insn->wait.has_timeout) { 1235 | vm->waiting_for = 1; 1236 | 1237 | if (unlikely(get_monotonic_time(&now))) { 1238 | return EXEC_NOMEM; 1239 | } 1240 | 1241 | vm->wait_for.start = now; 1242 | vm->wait_for.interval = tm_val; 1243 | } else if (insn->wait.func) { 1244 | vm->waiting_until = 1; 1245 | vm->wait_until.func = insn->wait.func; 1246 | vm->wait_until.id = insn->wait.wlab_id; 1247 | } 1248 | 1249 | qvm->parent = vm; 1250 | vm = qvm; 1251 | return EXEC_OK; 1252 | } 1253 | 1254 | static int insn_wlab(const struct codegen_insn *const insn) 1255 | { 1256 | assert(insn->op == CODEGEN_OP_WLAB); 1257 | vm->last_passed.func = insn->wlab.func; 1258 | vm->last_passed.id = insn->wlab.id; 1259 | return EXEC_OK; 1260 | } 1261 | 1262 | static int insn_getsp(const struct codegen_insn *const insn) 1263 | { 1264 | assert(insn->op == CODEGEN_OP_GETSP); 1265 | 1266 | const uint8_t dst_is_temp = insn->dst.opd == CODEGEN_OPD_TEMP; 1267 | const uint8_t dst_signd = insn->dst.signd; 1268 | const uint8_t dst_indirect = insn->dst.indirect; 1269 | const uint64_t dst_size = opd_size(&insn->dst); 1270 | 1271 | LEGAL_IF(dst_is_temp == 1, "%u", dst_is_temp); 1272 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 1273 | LEGAL_IF(dst_indirect == 0, "%u", dst_indirect); 1274 | LEGAL_IF(dst_size == 8, "%" PRIu64, dst_size); 1275 | 1276 | *((uint64_t *) opd_val(&insn->dst)) = vm->sp; 1277 | return EXEC_OK; 1278 | } 1279 | 1280 | static int insn_qnt(const struct codegen_insn *const insn) 1281 | { 1282 | assert(insn->op == CODEGEN_OP_QNT); 1283 | 1284 | const uint8_t dst_signd = insn->qnt.dst.signd; 1285 | const uint8_t dst_indirect = insn->qnt.dst.indirect; 1286 | const uint64_t dst_size = opd_size(&insn->qnt.dst); 1287 | 1288 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 1289 | LEGAL_IF(dst_indirect == 0, "%u", dst_indirect); 1290 | LEGAL_IF(dst_size == 8, "%" PRIu64, dst_size); 1291 | 1292 | const uint8_t loc_signd = insn->qnt.loc.signd; 1293 | const uint8_t loc_indirect = insn->qnt.loc.indirect; 1294 | const uint64_t loc_size = opd_size(&insn->qnt.loc); 1295 | 1296 | LEGAL_IF(loc_signd == 0, "%u", loc_signd); 1297 | LEGAL_IF(loc_indirect == 0 || loc_indirect == 1, "%u", loc_indirect); 1298 | LEGAL_IF(loc_size == 8, "%" PRIu64, loc_size); 1299 | 1300 | const uint8_t ssp_is_temp = insn->qnt.sp.opd == CODEGEN_OPD_TEMP; 1301 | const uint8_t ssp_signd = insn->qnt.sp.signd; 1302 | const uint8_t ssp_indirect = insn->qnt.sp.indirect; 1303 | const uint64_t ssp_size = opd_size(&insn->qnt.sp); 1304 | 1305 | LEGAL_IF(ssp_is_temp == 1, "%u", ssp_is_temp); 1306 | LEGAL_IF(ssp_signd == 0, "%u", ssp_signd); 1307 | LEGAL_IF(ssp_indirect == 0, "%u", ssp_indirect); 1308 | LEGAL_IF(ssp_size == 8, "%" PRIu64, ssp_size); 1309 | 1310 | uint64_t *const dst = opd_val(&insn->qnt.dst); 1311 | const uint64_t loc = *(uint64_t *) opd_val(&insn->qnt.loc); 1312 | const uint64_t ssp = *(uint64_t *) opd_val(&insn->qnt.sp); 1313 | 1314 | struct qvm *const qvm = calloc(1, sizeof(struct qvm) + STACK_SIZE); 1315 | 1316 | if (!qvm) { 1317 | return EXEC_NOMEM; 1318 | } 1319 | 1320 | qvm->ip = loc; 1321 | qvm->bp = 8 * 2; 1322 | qvm->sp = qvm->bp + vm->sp - ssp; 1323 | qvm->at_start = 1; 1324 | memcpy(qvm->stack + qvm->bp, vm->stack + ssp, (size_t) (vm->sp - ssp)); 1325 | *dst = (uint64_t) (uintptr_t) qvm; 1326 | vm->sp = ssp; 1327 | return EXEC_OK; 1328 | } 1329 | 1330 | static int insn_qntv(const struct codegen_insn *const insn) 1331 | { 1332 | assert(insn->op == CODEGEN_OP_QNTV); 1333 | 1334 | const uint8_t dst_signd = insn->qntv.dst.signd; 1335 | const uint8_t dst_indirect = insn->qntv.dst.indirect; 1336 | const uint64_t dst_size = opd_size(&insn->qntv.dst); 1337 | 1338 | LEGAL_IF(dst_signd == 0, "%u", dst_signd); 1339 | LEGAL_IF(dst_indirect == 0, "%u", dst_indirect); 1340 | LEGAL_IF(dst_size == 8, "%" PRIu64, dst_size); 1341 | 1342 | const uint8_t val_signd = insn->qntv.val.signd; 1343 | const uint8_t val_indirect = insn->qntv.val.indirect; 1344 | const uint64_t val_size = opd_size(&insn->qntv.val); 1345 | 1346 | LEGAL_IF(val_signd == 0 || val_signd == 1, "%u", val_signd); 1347 | LEGAL_IF(val_indirect == 0 || val_indirect == 1, "%u", val_indirect); 1348 | LEGAL_IF(val_size > 0, "%" PRIu64, val_size); 1349 | 1350 | uint64_t *const dst = opd_val(&insn->qntv.dst); 1351 | const void *const val = opd_val(&insn->qntv.val); 1352 | 1353 | struct qvm *const qvm = calloc(1, sizeof(struct qvm) + STACK_SIZE); 1354 | 1355 | if (!qvm) { 1356 | return EXEC_NOMEM; 1357 | } 1358 | 1359 | qvm->at_start = 1; 1360 | qvm->at_end = 1; 1361 | memcpy(qvm->stack, val, (size_t) val_size); 1362 | *dst = (uint64_t) (uintptr_t) qvm; 1363 | return EXEC_OK; 1364 | } 1365 | 1366 | static int insn_noint_int(const struct codegen_insn *const insn) 1367 | { 1368 | assert(insn->op == CODEGEN_OP_NOINT || insn->op == CODEGEN_OP_INT); 1369 | vm->noint = insn->op == CODEGEN_OP_NOINT; 1370 | return EXEC_OK; 1371 | } 1372 | 1373 | static int insn_bfun(const struct codegen_insn *const insn) 1374 | { 1375 | assert(insn->op == CODEGEN_OP_BFUN); 1376 | 1377 | LEGAL_IF(vm->sp % 8 == 0, "%" PRIu64, vm->sp); 1378 | LEGAL_IF(vm->bp % 8 == 0, "%" PRIu64, vm->bp); 1379 | LEGAL_IF(vm->sp >= 16, "%" PRIu64, vm->sp); 1380 | 1381 | uint64_t retval_size = 0; 1382 | const void *retval = NULL; 1383 | 1384 | switch (vm->ip) { 1385 | case SCOPE_BFUN_ID_NULL: 1386 | LEGAL_IF(0, "null function call"); 1387 | break; 1388 | 1389 | case SCOPE_BFUN_ID_MONOTIME: 1390 | if (unlikely(get_monotonic_time(&now))) { 1391 | return EXEC_NOMEM; 1392 | } 1393 | 1394 | retval_size = sizeof(now); 1395 | retval = &now; 1396 | break; 1397 | 1398 | case SCOPE_BFUN_ID_MALLOC: 1399 | case SCOPE_BFUN_ID_CALLOC: { 1400 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1401 | LEGAL_IF(vm->bp + 8 <= STACK_SIZE, "%" PRIu64, vm->bp); 1402 | retval_size = 8; 1403 | const size_t size = (size_t) *(uint64_t *) (vm->stack + vm->bp); 1404 | 1405 | const uint64_t mem = vm->ip == SCOPE_BFUN_ID_MALLOC ? 1406 | (uint64_t) (uintptr_t) malloc(size) : (uint64_t) (uintptr_t) calloc(1, size); 1407 | 1408 | retval = &mem; 1409 | vm->sp -= 8; 1410 | } break; 1411 | 1412 | case SCOPE_BFUN_ID_REALLOC: { 1413 | LEGAL_IF(vm->sp >= 16 + 16, "%" PRIu64, vm->sp); 1414 | LEGAL_IF(vm->bp + 16 <= STACK_SIZE, "%" PRIu64, vm->bp); 1415 | retval_size = 8; 1416 | void *const oldptr = (void *) (uintptr_t) *(uint64_t *) (vm->stack + vm->bp); 1417 | const size_t newsize = (size_t) *(uint64_t *) (vm->stack + vm->bp + 8); 1418 | const uint64_t mem = (uint64_t) (uintptr_t) realloc(oldptr, newsize); 1419 | retval = &mem; 1420 | vm->sp -= 16; 1421 | } break; 1422 | 1423 | case SCOPE_BFUN_ID_FREE: { 1424 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1425 | LEGAL_IF(vm->bp + 8 <= STACK_SIZE, "%" PRIu64, vm->bp); 1426 | void *const ptr = (void *) (uintptr_t) *(uint64_t *) (vm->stack + vm->bp); 1427 | free(ptr); 1428 | vm->sp -= 8; 1429 | } break; 1430 | 1431 | case SCOPE_BFUN_ID_PS: 1432 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1433 | LEGAL_IF(vm->bp + 8 <= STACK_SIZE, "%" PRIu64, vm->bp); 1434 | printf("%s", (const char *) (uintptr_t) *(uint64_t *) (vm->stack + vm->bp)); 1435 | fflush(stdout); 1436 | vm->sp -= 8; 1437 | break; 1438 | 1439 | case SCOPE_BFUN_ID_PU8: 1440 | case SCOPE_BFUN_ID_PI8: 1441 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1442 | LEGAL_IF(vm->bp + 1 <= STACK_SIZE, "%" PRIu64, vm->bp); 1443 | 1444 | vm->ip == SCOPE_BFUN_ID_PU8 ? 1445 | printf("%" PRIu8, *(uint8_t *) (vm->stack + vm->bp)) : 1446 | printf("%" PRIi8, *(int8_t *) (vm->stack + vm->bp)); 1447 | 1448 | fflush(stdout); 1449 | vm->sp -= 8; 1450 | break; 1451 | 1452 | case SCOPE_BFUN_ID_PU16: 1453 | case SCOPE_BFUN_ID_PI16: 1454 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1455 | LEGAL_IF(vm->bp + 2 <= STACK_SIZE, "%" PRIu64, vm->bp); 1456 | 1457 | vm->ip == SCOPE_BFUN_ID_PU16 ? 1458 | printf("%" PRIu16, *(uint16_t *) (vm->stack + vm->bp)) : 1459 | printf("%" PRIi16, *(int16_t *) (vm->stack + vm->bp)); 1460 | 1461 | fflush(stdout); 1462 | vm->sp -= 8; 1463 | break; 1464 | 1465 | case SCOPE_BFUN_ID_PU32: 1466 | case SCOPE_BFUN_ID_PI32: 1467 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1468 | LEGAL_IF(vm->bp + 4 <= STACK_SIZE, "%" PRIu64, vm->bp); 1469 | 1470 | vm->ip == SCOPE_BFUN_ID_PU32 ? 1471 | printf("%" PRIu32, *(uint32_t *) (vm->stack + vm->bp)) : 1472 | printf("%" PRIi32, *(int32_t *) (vm->stack + vm->bp)); 1473 | 1474 | fflush(stdout); 1475 | vm->sp -= 8; 1476 | break; 1477 | 1478 | case SCOPE_BFUN_ID_PU64: 1479 | case SCOPE_BFUN_ID_PI64: 1480 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1481 | LEGAL_IF(vm->bp + 8 <= STACK_SIZE, "%" PRIu64, vm->bp); 1482 | 1483 | vm->ip == SCOPE_BFUN_ID_PU64 ? 1484 | printf("%" PRIu64, *(uint64_t *) (vm->stack + vm->bp)) : 1485 | printf("%" PRIi64, *(int64_t *) (vm->stack + vm->bp)); 1486 | 1487 | fflush(stdout); 1488 | vm->sp -= 8; 1489 | break; 1490 | 1491 | case SCOPE_BFUN_ID_PNL: 1492 | printf("\n"); 1493 | fflush(stdout); 1494 | break; 1495 | 1496 | case SCOPE_BFUN_ID_EXIT: 1497 | LEGAL_IF(vm->sp >= 16 + 8, "%" PRIu64, vm->sp); 1498 | LEGAL_IF(vm->bp + 4 <= STACK_SIZE, "%" PRIu64, vm->bp); 1499 | vm->sp -= 8; 1500 | exit(*(int32_t *) (vm->stack + vm->bp)); 1501 | break; 1502 | } 1503 | 1504 | vm->sp -= 16; 1505 | struct tmp_frame *const new_tmp_frame = malloc(sizeof(struct tmp_frame)); 1506 | 1507 | if (unlikely(!new_tmp_frame)) { 1508 | return EXEC_NOMEM; 1509 | } 1510 | 1511 | new_tmp_frame->prev = vm->temps; 1512 | vm->temps = new_tmp_frame; 1513 | 1514 | return handle_return(insn, retval_size, retval); 1515 | } 1516 | 1517 | static int exec_insn(const struct codegen_insn *const insn) 1518 | { 1519 | int result = EXEC_OK; 1520 | 1521 | switch (insn->op) { 1522 | case CODEGEN_OP_NOP: 1523 | result = EXEC_OK; 1524 | break; 1525 | 1526 | case CODEGEN_OP_MOV: 1527 | result = insn_mov(insn); 1528 | break; 1529 | 1530 | case CODEGEN_OP_CAST: 1531 | result = insn_cast(insn); 1532 | break; 1533 | 1534 | case CODEGEN_OP_ADD: 1535 | case CODEGEN_OP_SUB: 1536 | case CODEGEN_OP_MUL: 1537 | case CODEGEN_OP_DIV: 1538 | case CODEGEN_OP_MOD: 1539 | case CODEGEN_OP_LSH: 1540 | case CODEGEN_OP_RSH: 1541 | case CODEGEN_OP_AND: 1542 | case CODEGEN_OP_XOR: 1543 | case CODEGEN_OP_OR: 1544 | result = insn_bin_arith(insn); 1545 | break; 1546 | 1547 | case CODEGEN_OP_EQU: 1548 | case CODEGEN_OP_NEQ: 1549 | result = insn_equ_neq(insn); 1550 | break; 1551 | 1552 | case CODEGEN_OP_LT: 1553 | case CODEGEN_OP_GT: 1554 | case CODEGEN_OP_LTE: 1555 | case CODEGEN_OP_GTE: 1556 | result = insn_bin_logic(insn); 1557 | break; 1558 | 1559 | case CODEGEN_OP_NOT: 1560 | result = insn_not(insn); 1561 | break; 1562 | 1563 | case CODEGEN_OP_NEG: 1564 | result = insn_neg(insn); 1565 | break; 1566 | 1567 | case CODEGEN_OP_BNEG: 1568 | result = insn_bneg(insn); 1569 | break; 1570 | 1571 | case CODEGEN_OP_OZ: 1572 | result = insn_oz(insn); 1573 | break; 1574 | 1575 | case CODEGEN_OP_INC: 1576 | case CODEGEN_OP_DEC: 1577 | result = insn_inc_dec(insn); 1578 | break; 1579 | 1580 | case CODEGEN_OP_INCP: 1581 | case CODEGEN_OP_DECP: 1582 | result = insn_incp_decp(insn); 1583 | break; 1584 | 1585 | case CODEGEN_OP_JZ: 1586 | case CODEGEN_OP_JNZ: 1587 | result = insn_cjmp(insn); 1588 | break; 1589 | 1590 | case CODEGEN_OP_JMP: 1591 | result = insn_jmp(insn); 1592 | break; 1593 | 1594 | case CODEGEN_OP_PUSHR: 1595 | result = insn_pushr(insn); 1596 | break; 1597 | 1598 | case CODEGEN_OP_PUSH: 1599 | result = insn_push(insn); 1600 | break; 1601 | 1602 | case CODEGEN_OP_CALL: 1603 | case CODEGEN_OP_CALLV: 1604 | result = insn_call_callv(insn); 1605 | break; 1606 | 1607 | case CODEGEN_OP_INCSP: 1608 | result = insn_incsp(insn); 1609 | break; 1610 | 1611 | case CODEGEN_OP_RET: 1612 | case CODEGEN_OP_RETV: 1613 | result = insn_ret_retv(insn); 1614 | break; 1615 | 1616 | case CODEGEN_OP_REF: 1617 | result = insn_ref(insn); 1618 | break; 1619 | 1620 | case CODEGEN_OP_DRF: 1621 | result = insn_drf(insn); 1622 | break; 1623 | 1624 | case CODEGEN_OP_RTE: 1625 | case CODEGEN_OP_RTEV: 1626 | result = insn_rte_rtev(insn); 1627 | break; 1628 | 1629 | case CODEGEN_OP_QAT: 1630 | result = insn_qat(insn); 1631 | break; 1632 | 1633 | case CODEGEN_OP_WAIT: 1634 | result = insn_wait(insn); 1635 | break; 1636 | 1637 | case CODEGEN_OP_WLAB: 1638 | result = insn_wlab(insn); 1639 | break; 1640 | 1641 | case CODEGEN_OP_GETSP: 1642 | result = insn_getsp(insn); 1643 | break; 1644 | 1645 | case CODEGEN_OP_QNT: 1646 | result = insn_qnt(insn); 1647 | break; 1648 | 1649 | case CODEGEN_OP_QNTV: 1650 | result = insn_qntv(insn); 1651 | break; 1652 | 1653 | case CODEGEN_OP_NOINT: 1654 | case CODEGEN_OP_INT: 1655 | result = insn_noint_int(insn); 1656 | break; 1657 | 1658 | case CODEGEN_OP_BFUN: 1659 | result = insn_bfun(insn); 1660 | break; 1661 | 1662 | default: LEGAL_IF(false, "unknown instruction: %u", insn->op); 1663 | } 1664 | 1665 | switch (insn->op) { 1666 | case CODEGEN_OP_JZ: 1667 | case CODEGEN_OP_JNZ: 1668 | case CODEGEN_OP_JMP: 1669 | case CODEGEN_OP_CALL: 1670 | case CODEGEN_OP_CALLV: 1671 | case CODEGEN_OP_RET: 1672 | case CODEGEN_OP_RETV: 1673 | case CODEGEN_OP_BFUN: 1674 | case CODEGEN_OP_RTE: 1675 | case CODEGEN_OP_RTEV: 1676 | case CODEGEN_OP_WAIT: 1677 | break; 1678 | 1679 | default: 1680 | vm->ip++; 1681 | } 1682 | 1683 | check_and_eventually_split_vms(); 1684 | return result; 1685 | } 1686 | 1687 | int exec(const struct codegen_obj *const obj) 1688 | { 1689 | if (!(bss = calloc(1, obj->data_size + obj->strings.size))) { 1690 | return EXEC_NOMEM; 1691 | } 1692 | 1693 | if (!(vm = calloc(1, sizeof(struct qvm) + STACK_SIZE))) { 1694 | free(bss); 1695 | return EXEC_NOMEM; 1696 | } 1697 | 1698 | if (obj->strings.mem) { 1699 | memcpy(bss + obj->data_size, obj->strings.mem, obj->strings.size); 1700 | } 1701 | 1702 | bss_size = obj->data_size + obj->strings.size; 1703 | *(uint64_t *) vm->stack = obj->insn_count; 1704 | vm->sp = vm->bp = 16; 1705 | vm->ip = SCOPE_BFUN_ID_COUNT; 1706 | 1707 | o = obj; 1708 | int error; 1709 | 1710 | do { 1711 | if ((error = exec_insn(&o->insns[vm->ip]))) { 1712 | break; 1713 | } 1714 | 1715 | LEGAL_IF(vm->ip <= o->insn_count, "%" PRIu64, vm->ip); 1716 | } while (vm->ip < o->insn_count); 1717 | 1718 | free(vm); 1719 | free(bss); 1720 | return error; 1721 | } 1722 | -------------------------------------------------------------------------------- /src/exec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct codegen_obj; 4 | 5 | int exec(const struct codegen_obj *); 6 | 7 | enum { 8 | EXEC_OK = 0, 9 | EXEC_NOMEM, 10 | EXEC_ILLEGAL, 11 | }; 12 | -------------------------------------------------------------------------------- /src/htab.c: -------------------------------------------------------------------------------- 1 | #include "htab.h" 2 | 3 | #include "common.h" 4 | 5 | #include 6 | #include 7 | 8 | #define HASH(key, count) ((key) % ((count) ?: 1)) 9 | 10 | int htab_create(struct htab **const htab, const size_t count) 11 | { 12 | assert(htab != NULL); 13 | *htab = calloc(1, sizeof(struct htab) + count * sizeof((*htab)->items[0])); 14 | 15 | if (unlikely(!*htab)) { 16 | return HTAB_NOMEM; 17 | } 18 | 19 | (*htab)->count = count; 20 | return HTAB_OK; 21 | } 22 | 23 | void htab_destroy(const struct htab *const htab, void (*const dtor)(const void *)) 24 | { 25 | if (likely(htab && dtor)) { 26 | for (size_t idx = 0; idx < htab->count; ++idx) { 27 | dtor(htab->items[idx].data); 28 | } 29 | } 30 | 31 | free((struct htab *) htab); 32 | } 33 | 34 | void htab_default_dtor(const void *const data) 35 | { 36 | free((void *) data); 37 | } 38 | 39 | void htab_insert(struct htab *const htab, const uintptr_t key, const void *const data) 40 | { 41 | assert(htab != NULL); 42 | assert("zero key passed" && key); 43 | 44 | const size_t pos = HASH(key, htab->count); 45 | size_t idx; 46 | 47 | for (idx = pos; idx < htab->count; ++idx) { 48 | assert("duplicate key" && htab->items[idx].key != key); 49 | 50 | if (likely(!htab->items[idx].key)) { 51 | goto insert; 52 | } 53 | } 54 | 55 | for (idx = 0; idx < pos; ++idx) { 56 | assert("duplicate key" && htab->items[idx].key != key); 57 | 58 | if (likely(!htab->items[idx].key)) { 59 | goto insert; 60 | } 61 | } 62 | 63 | assert(0), abort(); 64 | 65 | insert: 66 | htab->items[idx].key = key; 67 | htab->items[idx].data = data; 68 | } 69 | 70 | void *htab_get(const struct htab *const htab, const uintptr_t key) 71 | { 72 | assert(htab != NULL); 73 | assert("zero key passed" && key); 74 | 75 | const size_t pos = HASH(key, htab->count); 76 | size_t idx; 77 | 78 | for (idx = pos; idx < htab->count; ++idx) { 79 | if (likely(htab->items[idx].key == key)) { 80 | goto get; 81 | } 82 | } 83 | 84 | for (idx = 0; idx < pos; ++idx) { 85 | if (likely(htab->items[idx].key == key)) { 86 | goto get; 87 | } 88 | } 89 | 90 | assert(0), abort(); 91 | 92 | get: 93 | return (void *) htab->items[idx].data; 94 | } 95 | -------------------------------------------------------------------------------- /src/htab.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct htab { 7 | size_t count; 8 | 9 | struct { 10 | uintptr_t key; 11 | const void *data; 12 | } items[]; 13 | }; 14 | 15 | int htab_create(struct htab **, size_t); 16 | void htab_destroy(const struct htab *, void (*)(const void *)); 17 | void htab_default_dtor(const void *); 18 | void htab_insert(struct htab *, uintptr_t, const void *); 19 | void *htab_get(const struct htab *, uintptr_t); 20 | 21 | enum { 22 | HTAB_OK = 0, 23 | HTAB_NOMEM, 24 | }; 25 | -------------------------------------------------------------------------------- /src/lex.c: -------------------------------------------------------------------------------- 1 | #include "lex.h" 2 | 3 | #include "common.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | enum { 10 | STS_ACCEPT, 11 | STS_REJECT, 12 | STS_HUNGRY, 13 | }; 14 | 15 | typedef uint8_t sts_t; 16 | 17 | #define TR(st, tr) (*s = (st), (STS_##tr)) 18 | #define REJECT TR(0, REJECT) 19 | 20 | #define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) 21 | #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') 22 | #define IS_ALNUM(c) (IS_ALPHA(c) || IS_DIGIT(c)) 23 | #define IS_WSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n') 24 | 25 | #define TOKEN_DEFINE_1(token, str) \ 26 | static sts_t token(const uint8_t c, uint8_t *const s) \ 27 | { \ 28 | switch (*s) { \ 29 | case 0: return c == (str)[0] ? TR(1, ACCEPT) : REJECT; \ 30 | case 1: return REJECT; \ 31 | default: assert(0), abort(); \ 32 | } \ 33 | } 34 | 35 | #define TOKEN_DEFINE_2(token, str) \ 36 | static sts_t token(const uint8_t c, uint8_t *const s) \ 37 | { \ 38 | switch (*s) { \ 39 | case 0: return c == (str)[0] ? TR(1, HUNGRY) : REJECT; \ 40 | case 1: return c == (str)[1] ? TR(2, ACCEPT) : REJECT; \ 41 | case 2: return REJECT; \ 42 | default: assert(0), abort(); \ 43 | } \ 44 | } 45 | 46 | #define TOKEN_DEFINE_3(token, str) \ 47 | static sts_t token(const uint8_t c, uint8_t *const s) \ 48 | { \ 49 | switch (*s) { \ 50 | case 0: return c == (str)[0] ? TR(1, HUNGRY) : REJECT; \ 51 | case 1: return c == (str)[1] ? TR(2, HUNGRY) : REJECT; \ 52 | case 2: return c == (str)[2] ? TR(3, ACCEPT) : REJECT; \ 53 | case 3: return REJECT; \ 54 | default: assert(0), abort(); \ 55 | } \ 56 | } 57 | 58 | #define TOKEN_DEFINE_4(token, str) \ 59 | static sts_t token(const uint8_t c, uint8_t *const s) \ 60 | { \ 61 | switch (*s) { \ 62 | case 0: return c == (str)[0] ? TR(1, HUNGRY) : REJECT; \ 63 | case 1: return c == (str)[1] ? TR(2, HUNGRY) : REJECT; \ 64 | case 2: return c == (str)[2] ? TR(3, HUNGRY) : REJECT; \ 65 | case 3: return c == (str)[3] ? TR(4, ACCEPT) : REJECT; \ 66 | case 4: return REJECT; \ 67 | default: assert(0), abort(); \ 68 | } \ 69 | } 70 | 71 | #define TOKEN_DEFINE_5(token, str) \ 72 | static sts_t token(const uint8_t c, uint8_t *const s) \ 73 | { \ 74 | switch (*s) { \ 75 | case 0: return c == (str)[0] ? TR(1, HUNGRY) : REJECT; \ 76 | case 1: return c == (str)[1] ? TR(2, HUNGRY) : REJECT; \ 77 | case 2: return c == (str)[2] ? TR(3, HUNGRY) : REJECT; \ 78 | case 3: return c == (str)[3] ? TR(4, HUNGRY) : REJECT; \ 79 | case 4: return c == (str)[4] ? TR(5, ACCEPT) : REJECT; \ 80 | case 5: return REJECT; \ 81 | default: assert(0), abort(); \ 82 | } \ 83 | } 84 | 85 | #define TOKEN_DEFINE_6(token, str) \ 86 | static sts_t token(const uint8_t c, uint8_t *const s) \ 87 | { \ 88 | switch (*s) { \ 89 | case 0: return c == (str)[0] ? TR(1, HUNGRY) : REJECT; \ 90 | case 1: return c == (str)[1] ? TR(2, HUNGRY) : REJECT; \ 91 | case 2: return c == (str)[2] ? TR(3, HUNGRY) : REJECT; \ 92 | case 3: return c == (str)[3] ? TR(4, HUNGRY) : REJECT; \ 93 | case 4: return c == (str)[4] ? TR(5, HUNGRY) : REJECT; \ 94 | case 5: return c == (str)[5] ? TR(6, ACCEPT) : REJECT; \ 95 | case 6: return REJECT; \ 96 | default: assert(0), abort(); \ 97 | } \ 98 | } 99 | 100 | #define TOKEN_DEFINE_7(token, str) \ 101 | static sts_t token(const uint8_t c, uint8_t *const s) \ 102 | { \ 103 | switch (*s) { \ 104 | case 0: return c == (str)[0] ? TR(1, HUNGRY) : REJECT; \ 105 | case 1: return c == (str)[1] ? TR(2, HUNGRY) : REJECT; \ 106 | case 2: return c == (str)[2] ? TR(3, HUNGRY) : REJECT; \ 107 | case 3: return c == (str)[3] ? TR(4, HUNGRY) : REJECT; \ 108 | case 4: return c == (str)[4] ? TR(5, HUNGRY) : REJECT; \ 109 | case 5: return c == (str)[5] ? TR(6, HUNGRY) : REJECT; \ 110 | case 6: return c == (str)[6] ? TR(7, ACCEPT) : REJECT; \ 111 | case 7: return REJECT; \ 112 | default: assert(0), abort(); \ 113 | } \ 114 | } 115 | 116 | static sts_t tk_name(const uint8_t c, uint8_t *const s) 117 | { 118 | enum { 119 | tk_name_begin, 120 | tk_name_accum, 121 | }; 122 | 123 | switch (*s) { 124 | case tk_name_begin: 125 | return IS_ALPHA(c) || (c == '_') ? TR(tk_name_accum, ACCEPT) : REJECT; 126 | 127 | case tk_name_accum: 128 | return IS_ALNUM(c) || (c == '_') ? STS_ACCEPT : REJECT; 129 | } 130 | 131 | assert(0), abort(); 132 | } 133 | 134 | static sts_t tk_nmbr(const uint8_t c, __attribute__((unused)) uint8_t *const s) 135 | { 136 | return IS_DIGIT(c) ? STS_ACCEPT : STS_REJECT; 137 | } 138 | 139 | static sts_t tk_strl(const uint8_t c, uint8_t *const s) 140 | { 141 | enum { 142 | tk_strl_begin, 143 | tk_strl_accum, 144 | tk_strl_end, 145 | }; 146 | 147 | switch (*s) { 148 | case tk_strl_begin: 149 | return c == '"' ? TR(tk_strl_accum, HUNGRY) : REJECT; 150 | 151 | case tk_strl_accum: 152 | return c != '"' ? STS_HUNGRY : TR(tk_strl_end, ACCEPT); 153 | 154 | case tk_strl_end: 155 | return REJECT; 156 | } 157 | 158 | assert(0), abort(); 159 | } 160 | 161 | static sts_t tk_wspc(const uint8_t c, uint8_t *const s) 162 | { 163 | enum { 164 | tk_wspc_begin, 165 | tk_wspc_accum, 166 | }; 167 | 168 | switch (*s) { 169 | case tk_wspc_begin: 170 | return IS_WSPACE(c) ? TR(tk_wspc_accum, ACCEPT) : REJECT; 171 | 172 | case tk_wspc_accum: 173 | return IS_WSPACE(c) ? STS_ACCEPT : REJECT; 174 | } 175 | 176 | assert(0), abort(); 177 | } 178 | 179 | static sts_t tk_lcom(const uint8_t c, uint8_t *const s) 180 | { 181 | enum { 182 | tk_lcom_begin, 183 | tk_lcom_first_slash, 184 | tk_lcom_accum, 185 | tk_lcom_end 186 | }; 187 | 188 | switch (*s) { 189 | case tk_lcom_begin: 190 | return c == '/' ? TR(tk_lcom_first_slash, HUNGRY) : REJECT; 191 | 192 | case tk_lcom_first_slash: 193 | return c == '/' ? TR(tk_lcom_accum, HUNGRY) : REJECT; 194 | 195 | case tk_lcom_accum: 196 | return c == '\n' || c == '\r' ? TR(tk_lcom_end, ACCEPT) : STS_HUNGRY; 197 | 198 | case tk_lcom_end: 199 | return REJECT; 200 | } 201 | 202 | assert(0), abort(); 203 | } 204 | 205 | static sts_t tk_bcom(const uint8_t c, uint8_t *const s) 206 | { 207 | enum { 208 | tk_bcom_begin, 209 | tk_bcom_open_slash, 210 | tk_bcom_accum, 211 | tk_bcom_close_star, 212 | tk_bcom_end 213 | }; 214 | 215 | switch (*s) { 216 | case tk_bcom_begin: 217 | return c == '/' ? TR(tk_bcom_open_slash, HUNGRY) : REJECT; 218 | 219 | case tk_bcom_open_slash: 220 | return c == '*' ? TR(tk_bcom_accum, HUNGRY) : REJECT; 221 | 222 | case tk_bcom_accum: 223 | return c != '*' ? STS_HUNGRY : TR(tk_bcom_close_star, HUNGRY); 224 | 225 | case tk_bcom_close_star: 226 | return c == '/' ? TR(tk_bcom_end, ACCEPT) : TR(tk_bcom_accum, HUNGRY); 227 | 228 | case tk_bcom_end: 229 | return REJECT; 230 | } 231 | 232 | assert(0), abort(); 233 | } 234 | 235 | TOKEN_DEFINE_1(tk_lpar, "(") 236 | TOKEN_DEFINE_1(tk_rpar, ")") 237 | TOKEN_DEFINE_1(tk_lbra, "[") 238 | TOKEN_DEFINE_1(tk_rbra, "]") 239 | TOKEN_DEFINE_1(tk_lbrc, "{") 240 | TOKEN_DEFINE_1(tk_rbrc, "}") 241 | 242 | TOKEN_DEFINE_2(tk_cond, "if") 243 | TOKEN_DEFINE_4(tk_elif, "elif") 244 | TOKEN_DEFINE_4(tk_else, "else") 245 | TOKEN_DEFINE_2(tk_dowh, "do") 246 | TOKEN_DEFINE_5(tk_whil, "while") 247 | TOKEN_DEFINE_6(tk_retn, "return") 248 | TOKEN_DEFINE_3(tk_useu, "use") 249 | TOKEN_DEFINE_4(tk_type, "type") 250 | 251 | TOKEN_DEFINE_1(tk_assn, "=") 252 | TOKEN_DEFINE_2(tk_aspl, "+=") 253 | TOKEN_DEFINE_2(tk_asmi, "-=") 254 | TOKEN_DEFINE_2(tk_asmu, "*=") 255 | TOKEN_DEFINE_2(tk_asdi, "/=") 256 | TOKEN_DEFINE_2(tk_asmo, "%=") 257 | TOKEN_DEFINE_3(tk_asls, "<<=") 258 | TOKEN_DEFINE_3(tk_asrs, ">>=") 259 | TOKEN_DEFINE_2(tk_asan, "&=") 260 | TOKEN_DEFINE_2(tk_asxo, "^=") 261 | TOKEN_DEFINE_2(tk_asor, "|=") 262 | TOKEN_DEFINE_1(tk_coln, ":") 263 | TOKEN_DEFINE_2(tk_scop, "::") 264 | TOKEN_DEFINE_1(tk_atsi, "@") 265 | TOKEN_DEFINE_1(tk_memb, ".") 266 | TOKEN_DEFINE_2(tk_arow, "->") 267 | TOKEN_DEFINE_2(tk_equl, "==") 268 | TOKEN_DEFINE_2(tk_neql, "!=") 269 | TOKEN_DEFINE_1(tk_lthn, "<") 270 | TOKEN_DEFINE_1(tk_gthn, ">") 271 | TOKEN_DEFINE_2(tk_lteq, "<=") 272 | TOKEN_DEFINE_2(tk_gteq, ">=") 273 | TOKEN_DEFINE_2(tk_conj, "&&") 274 | TOKEN_DEFINE_2(tk_disj, "||") 275 | TOKEN_DEFINE_1(tk_plus, "+") 276 | TOKEN_DEFINE_1(tk_mins, "-") 277 | TOKEN_DEFINE_1(tk_mult, "*") 278 | TOKEN_DEFINE_1(tk_divi, "/") 279 | TOKEN_DEFINE_1(tk_modu, "%") 280 | TOKEN_DEFINE_2(tk_lshf, "<<") 281 | TOKEN_DEFINE_2(tk_rshf, ">>") 282 | TOKEN_DEFINE_1(tk_amps, "&") 283 | TOKEN_DEFINE_1(tk_care, "^") 284 | TOKEN_DEFINE_1(tk_pipe, "|") 285 | TOKEN_DEFINE_1(tk_coma, ",") 286 | TOKEN_DEFINE_2(tk_cast, "as") 287 | TOKEN_DEFINE_1(tk_ques, "?") 288 | 289 | TOKEN_DEFINE_1(tk_excl, "!") 290 | TOKEN_DEFINE_1(tk_tild, "~") 291 | TOKEN_DEFINE_2(tk_incr, "++") 292 | TOKEN_DEFINE_2(tk_decr, "--") 293 | TOKEN_DEFINE_6(tk_szof, "sizeof") 294 | TOKEN_DEFINE_7(tk_alof, "alignof") 295 | 296 | TOKEN_DEFINE_4(tk_wait, "wait") 297 | TOKEN_DEFINE_3(tk_wfor, "for") 298 | TOKEN_DEFINE_5(tk_wunt, "until") 299 | TOKEN_DEFINE_7(tk_wnob, "noblock") 300 | TOKEN_DEFINE_4(tk_wmse, "msec") 301 | TOKEN_DEFINE_3(tk_wsec, "sec") 302 | TOKEN_DEFINE_5(tk_noin, "noint") 303 | 304 | TOKEN_DEFINE_1(tk_scol, ";") 305 | 306 | TOKEN_DEFINE_5(tk_cons, "const") 307 | TOKEN_DEFINE_7(tk_expo, "exposed") 308 | TOKEN_DEFINE_6(tk_stat, "static") 309 | 310 | static sts_t (*const tk_funcs[])(const uint8_t, uint8_t *const) = { 311 | tk_name, 312 | tk_nmbr, 313 | tk_strl, 314 | 315 | tk_wspc, 316 | tk_lcom, 317 | tk_bcom, 318 | 319 | tk_lpar, 320 | tk_rpar, 321 | tk_lbra, 322 | tk_rbra, 323 | tk_lbrc, 324 | tk_rbrc, 325 | 326 | tk_cond, 327 | tk_elif, 328 | tk_else, 329 | tk_dowh, 330 | tk_whil, 331 | tk_retn, 332 | tk_useu, 333 | tk_type, 334 | 335 | tk_assn, 336 | tk_aspl, 337 | tk_asmi, 338 | tk_asmu, 339 | tk_asdi, 340 | tk_asmo, 341 | tk_asls, 342 | tk_asrs, 343 | tk_asan, 344 | tk_asxo, 345 | tk_asor, 346 | tk_coln, 347 | tk_scop, 348 | tk_atsi, 349 | tk_memb, 350 | tk_arow, 351 | tk_equl, 352 | tk_neql, 353 | tk_lthn, 354 | tk_gthn, 355 | tk_lteq, 356 | tk_gteq, 357 | tk_conj, 358 | tk_disj, 359 | tk_plus, 360 | tk_mins, 361 | tk_mult, 362 | tk_divi, 363 | tk_modu, 364 | tk_lshf, 365 | tk_rshf, 366 | tk_amps, 367 | tk_care, 368 | tk_pipe, 369 | tk_coma, 370 | tk_cast, 371 | tk_ques, 372 | 373 | tk_excl, 374 | tk_tild, 375 | tk_incr, 376 | tk_decr, 377 | tk_szof, 378 | tk_alof, 379 | 380 | tk_wait, 381 | tk_wfor, 382 | tk_wunt, 383 | tk_wnob, 384 | tk_wmse, 385 | tk_wsec, 386 | tk_noin, 387 | 388 | tk_scol, 389 | 390 | tk_cons, 391 | tk_expo, 392 | tk_stat, 393 | }; 394 | 395 | static_assert(countof(tk_funcs) == LEX_TK_COUNT, "mismatch"); 396 | 397 | static inline int push_token(struct lex_token **const tokens, 398 | size_t *const ntokens, size_t *const allocated, const lex_tk_t token, 399 | const uint8_t *const beg, const uint8_t *const end) 400 | { 401 | if (unlikely(*ntokens >= *allocated)) { 402 | *allocated = (*allocated ?: 1) * 8; 403 | 404 | struct lex_token *const tmp = 405 | realloc(*tokens, *allocated * sizeof(struct lex_token)); 406 | 407 | if (unlikely(!tmp)) { 408 | return free(*tokens), *tokens = NULL, LEX_NOMEM; 409 | } 410 | 411 | *tokens = tmp; 412 | } 413 | 414 | (*tokens)[(*ntokens)++] = (struct lex_token) { 415 | .beg = beg, 416 | .end = end, 417 | .tk = token 418 | }; 419 | 420 | return LEX_OK; 421 | } 422 | 423 | const char *lex_current_file; 424 | 425 | int lex(const uint8_t *const input, const size_t size, 426 | struct lex_token **const tokens, size_t *const ntokens) 427 | { 428 | static struct { 429 | sts_t prev, curr; 430 | } statuses[LEX_TK_COUNT] = { 431 | [0 ... LEX_TK_COUNT - 1] = { STS_HUNGRY, STS_REJECT } 432 | }; 433 | 434 | uint8_t states[LEX_TK_COUNT] = {0}; 435 | 436 | const uint8_t *prefix_beg = input, *prefix_end = input; 437 | lex_tk_t accepted_token; 438 | size_t allocated = 0; 439 | *tokens = NULL, *ntokens = 0; 440 | 441 | #define PUSH_OR_NOMEM(tk, beg, end) \ 442 | if (unlikely(push_token(tokens, ntokens, &allocated, (tk), (beg), (end)))) { \ 443 | fprintf(stderr, "out of memory while lexing\n"); \ 444 | return LEX_NOMEM; \ 445 | } 446 | 447 | #define print_last_token lex_print_error(stderr, "unknown token", \ 448 | &(*tokens)[*ntokens - 1], &(*tokens)[*ntokens - 1]) 449 | 450 | #define foreach_tk \ 451 | for (lex_tk_t tk = 0; tk < LEX_TK_COUNT; ++tk) 452 | 453 | PUSH_OR_NOMEM(LEX_TK_FBEG, NULL, NULL); 454 | 455 | while (prefix_end < input + size) { 456 | int did_accept = 0; 457 | 458 | foreach_tk { 459 | if (statuses[tk].prev != STS_REJECT) { 460 | statuses[tk].curr = tk_funcs[tk](*prefix_end, &states[tk]); 461 | } 462 | 463 | if (statuses[tk].curr != STS_REJECT) { 464 | did_accept = 1; 465 | } 466 | } 467 | 468 | if (did_accept) { 469 | prefix_end++; 470 | 471 | foreach_tk { 472 | statuses[tk].prev = statuses[tk].curr; 473 | } 474 | } else { 475 | accepted_token = LEX_TK_COUNT; 476 | 477 | foreach_tk { 478 | if (statuses[tk].prev == STS_ACCEPT) { 479 | accepted_token = tk; 480 | } 481 | 482 | statuses[tk].prev = STS_HUNGRY; 483 | statuses[tk].curr = STS_REJECT; 484 | } 485 | 486 | PUSH_OR_NOMEM(accepted_token, prefix_beg, prefix_end); 487 | 488 | if (unlikely(accepted_token == LEX_TK_COUNT)) { 489 | (*tokens)[*ntokens - 1].end++; 490 | return print_last_token, LEX_UNKNOWN_TOKEN; 491 | } 492 | 493 | prefix_beg = prefix_end; 494 | } 495 | } 496 | 497 | accepted_token = LEX_TK_COUNT; 498 | 499 | foreach_tk { 500 | if (statuses[tk].curr == STS_ACCEPT) { 501 | accepted_token = tk; 502 | } 503 | 504 | statuses[tk].prev = STS_HUNGRY; 505 | statuses[tk].curr = STS_REJECT; 506 | } 507 | 508 | PUSH_OR_NOMEM(accepted_token, prefix_beg, prefix_end); 509 | 510 | if (unlikely(accepted_token == LEX_TK_COUNT)) { 511 | return print_last_token, LEX_UNKNOWN_TOKEN; 512 | } 513 | 514 | PUSH_OR_NOMEM(LEX_TK_FEND, NULL, NULL); 515 | return LEX_OK; 516 | 517 | #undef PUSH_OR_NOMEM 518 | #undef print_last_token 519 | #undef foreach_tk 520 | } 521 | 522 | bool lex_symbols_equal(const struct lex_symbol *const sym1, 523 | const struct lex_symbol *const sym2) 524 | { 525 | const size_t len1 = (size_t) (sym1->end - sym1->beg); 526 | const size_t len2 = (size_t) (sym2->end - sym2->beg); 527 | 528 | return len1 == len2 && !memcmp(sym1->beg, sym2->beg, len1); 529 | } 530 | 531 | void lex_locate_linecol(const struct lex_token *token, 532 | size_t *const line, size_t *const col) 533 | { 534 | *line = 1, *col = 1; 535 | 536 | for (--token; token->tk != LEX_TK_FBEG; --token) { 537 | const uint8_t *character = token->end - 1; 538 | 539 | do { 540 | if (*character == '\n' || *character == '\r') { 541 | ++*line; 542 | } else if (*line == 1) { 543 | ++*col; 544 | } 545 | } while (--character != token->beg - 1); 546 | } 547 | } 548 | 549 | void lex_print_symbol(FILE *const out, const char *const fmt, 550 | const struct lex_symbol *const sym) 551 | { 552 | const ptrdiff_t len = sym->end - sym->beg; 553 | fprintf(out, fmt ?: "%.*s", (int) len, sym->beg); 554 | } 555 | 556 | void lex_print_error(FILE *const out, const char *const desc, 557 | const struct lex_token *ltok, const struct lex_token *const rtok) 558 | { 559 | size_t line, col; 560 | lex_locate_linecol(ltok, &line, &col); 561 | fprintf(out, "%s:%zu:%zu: %s: ", lex_current_file, line, col, desc); 562 | 563 | for (; ltok <= rtok; ++ltok) { 564 | fprintf(out, RED("%.*s"), (int) (ltok->end - ltok->beg), ltok->beg); 565 | } 566 | 567 | fputs("\n", out); 568 | } 569 | -------------------------------------------------------------------------------- /src/lex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum { 10 | LEX_TK_NAME, // name 11 | LEX_TK_NMBR, // decimal integer literal 12 | LEX_TK_STRL, // string literal 13 | 14 | LEX_TK_WSPC, // whitespace 15 | LEX_TK_LCOM, // line comment 16 | LEX_TK_BCOM, // block comment 17 | 18 | LEX_TK_LPAR, // left parenthesis 19 | LEX_TK_RPAR, // right parenthesis 20 | LEX_TK_LBRA, // left bracket 21 | LEX_TK_RBRA, // right bracket 22 | LEX_TK_LBRC, // left brace 23 | LEX_TK_RBRC, // right brace 24 | 25 | LEX_TK_COND, // if keyword 26 | LEX_TK_ELIF, // elif keyword 27 | LEX_TK_ELSE, // else keyword 28 | LEX_TK_DOWH, // do keyword 29 | LEX_TK_WHIL, // while keyword 30 | LEX_TK_RETN, // return keyword 31 | LEX_TK_USEU, // use keyword 32 | LEX_TK_TYPE, // type keyword 33 | 34 | LEX_TK_ASSN, // (binary) assignment 35 | LEX_TK_ASPL, // (binary) assignment-plus 36 | LEX_TK_ASMI, // (binary) assignment-minus 37 | LEX_TK_ASMU, // (binary) assignment-multiply 38 | LEX_TK_ASDI, // (binary) assignment-divide 39 | LEX_TK_ASMO, // (binary) assignment-modulo 40 | LEX_TK_ASLS, // (binary) assignment-left shift 41 | LEX_TK_ASRS, // (binary) assignment-right shift 42 | LEX_TK_ASAN, // (binary) assignment-and 43 | LEX_TK_ASXO, // (binary) assignment-xor 44 | LEX_TK_ASOR, // (binary) assignment-or 45 | LEX_TK_COLN, // (binary) colon separator 46 | LEX_TK_SCOP, // (binary) scope 47 | LEX_TK_ATSI, // (binary) quaint is at label test 48 | LEX_TK_MEMB, // (binary) struct/union member 49 | LEX_TK_AROW, // (binary) deref struct/union member 50 | LEX_TK_EQUL, // (binary) equality test 51 | LEX_TK_NEQL, // (binary) inequality test 52 | LEX_TK_LTHN, // (binary) less than 53 | LEX_TK_GTHN, // (binary) greater than 54 | LEX_TK_LTEQ, // (binary) less than or equal 55 | LEX_TK_GTEQ, // (binary) greater than or equal 56 | LEX_TK_CONJ, // (binary) logical and 57 | LEX_TK_DISJ, // (binary) logical or 58 | LEX_TK_PLUS, // (binary, unary) addition, identity 59 | LEX_TK_MINS, // (binary, unary) subtraction, negation 60 | LEX_TK_MULT, // (binary, unary) multiplication, dereference/dequaintify 61 | LEX_TK_DIVI, // (binary) division 62 | LEX_TK_MODU, // (binary) modulo 63 | LEX_TK_LSHF, // (binary) left shift 64 | LEX_TK_RSHF, // (binary) right shift 65 | LEX_TK_AMPS, // (binary, unary) bitwise and, address-of 66 | LEX_TK_CARE, // (binary, unary) bitwise xor, bitwise negation 67 | LEX_TK_PIPE, // (binary) bitwise or 68 | LEX_TK_COMA, // (binary) comma operator/separator 69 | LEX_TK_CAST, // (binary) typecast 70 | LEX_TK_QUES, // (ternary) ?: conditional operator 71 | 72 | LEX_TK_EXCL, // (unary) logical not 73 | LEX_TK_TILD, // (unary) quaintify 74 | LEX_TK_INCR, // (unary) prefix/postfix increment 75 | LEX_TK_DECR, // (unary) prefix/postfix decrement 76 | LEX_TK_SZOF, // (unary) size of type 77 | LEX_TK_ALOF, // (unary) alignment of type 78 | 79 | LEX_TK_WAIT, // wait keyword 80 | LEX_TK_WFOR, // for keyword 81 | LEX_TK_WUNT, // until keyword 82 | LEX_TK_WNOB, // noblock keyword 83 | LEX_TK_WMSE, // msec keyword 84 | LEX_TK_WSEC, // sec keyword 85 | LEX_TK_NOIN, // noint keyword 86 | 87 | LEX_TK_SCOL, // semicolon statement separator 88 | 89 | LEX_TK_CONS, // const (variables only) 90 | LEX_TK_EXPO, // exposed (variables and functions) 91 | LEX_TK_STAT, // static (variables in function scope only) 92 | 93 | LEX_TK_COUNT, 94 | LEX_TK_FBEG, // beginning of file marker 95 | LEX_TK_FEND, // end of file marker 96 | }; 97 | 98 | static_assert(LEX_TK_COUNT + 2 <= UINT8_MAX, ""); 99 | typedef uint8_t lex_tk_t; 100 | 101 | #define lex_tk_is_assn(t) ({ \ 102 | const lex_tk_t t_once = (t); \ 103 | t_once >= LEX_TK_ASSN && t_once <= LEX_TK_ASOR; \ 104 | }) 105 | 106 | struct lex_symbol { 107 | const uint8_t *beg, *end; 108 | }; 109 | 110 | #define lex_sym(s) &(const struct lex_symbol) { \ 111 | .beg = (const uint8_t *) (s), \ 112 | .end = (const uint8_t *) (s) + sizeof(s) - 1, \ 113 | } 114 | 115 | struct lex_token { 116 | const uint8_t *beg, *end; 117 | lex_tk_t tk; 118 | }; 119 | 120 | extern const char *lex_current_file; 121 | int lex(const uint8_t *, size_t, struct lex_token **, size_t *); 122 | 123 | enum { 124 | LEX_OK = 0, 125 | LEX_NOMEM, 126 | LEX_UNKNOWN_TOKEN, 127 | }; 128 | 129 | bool lex_symbols_equal(const struct lex_symbol *, const struct lex_symbol *); 130 | void lex_locate_linecol(const struct lex_token *, size_t *, size_t *); 131 | void lex_print_symbol(FILE *, const char *, const struct lex_symbol *); 132 | void lex_print_error(FILE *, const char *, const struct lex_token *, 133 | const struct lex_token *); 134 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "lex.h" 2 | #include "parse.h" 3 | #include "ast.h" 4 | #include "scope.h" 5 | #include "type.h" 6 | #include "codegen.h" 7 | #include "exec.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | int main(int argc, char **argv) 19 | { 20 | int fd; 21 | size_t size; 22 | struct stat statbuf; 23 | int exit_status = EXIT_FAILURE; 24 | 25 | if (argc != 2) { 26 | fprintf(stderr, "Usage: %s \n", argv[0]); 27 | goto out_exit; 28 | } 29 | 30 | if ((fd = open(argv[1], O_RDONLY)) < 0) { 31 | perror("open"); 32 | goto out_exit; 33 | } 34 | 35 | if (fstat(fd, &statbuf) < 0) { 36 | perror("fstat"); 37 | goto out_close; 38 | } 39 | 40 | if ((size = (size_t) statbuf.st_size) == 0) { 41 | fprintf(stderr, "‘%s‘: file is empty\n", argv[1]); 42 | goto out_close; 43 | } 44 | 45 | const uint8_t *mapped = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0); 46 | 47 | if (mapped == MAP_FAILED) { 48 | perror("mmap"); 49 | goto out_close; 50 | } 51 | 52 | struct lex_token *tokens; 53 | size_t ntokens; 54 | lex_current_file = argv[1]; 55 | 56 | if (lex(mapped, size, &tokens, &ntokens)) { 57 | goto out_destroy_tokens; 58 | } 59 | 60 | const struct parse_node root = parse(tokens, ntokens); 61 | 62 | if (parse_error(root)) { 63 | goto out_destroy_tokens; 64 | } 65 | 66 | struct ast_node *ast; 67 | const int ast_error = ast_build(&root, &ast); 68 | parse_tree_destroy(root); 69 | 70 | if (ast_error == AST_NOMEM) { 71 | goto out_destroy_ast; 72 | } 73 | 74 | const int scope_error = scope_build(ast); 75 | 76 | if (scope_error == SCOPE_NOMEM) { 77 | goto out_destroy_ast; 78 | } 79 | 80 | const int type_check_error = type_check_ast(ast); 81 | //ast_print(stdout, ast, 0); 82 | 83 | if (ast_error || scope_error || type_check_error) { 84 | goto out_destroy_ast; 85 | } 86 | 87 | struct codegen_obj obj; 88 | const int codegen_error = codegen_obj_create(ast, &obj); 89 | 90 | if (codegen_error) { 91 | goto out_destroy_obj; 92 | } 93 | 94 | type_symtab_clear(); 95 | ast_destroy(ast), ast = NULL; 96 | free(tokens), tokens = NULL; 97 | munmap((uint8_t *) mapped, size), mapped = NULL; 98 | close(fd), fd = -1; 99 | exit_status = exec(&obj); 100 | 101 | out_destroy_obj: 102 | codegen_obj_destroy(&obj); 103 | 104 | out_destroy_ast: 105 | type_symtab_clear(); 106 | ast_destroy(ast); 107 | 108 | out_destroy_tokens: 109 | free(tokens); 110 | 111 | if (mapped) { 112 | munmap((uint8_t *) mapped, size); 113 | } 114 | 115 | out_close: 116 | if (fd != -1) { 117 | close(fd); 118 | } 119 | 120 | out_exit: 121 | return exit_status; 122 | } 123 | -------------------------------------------------------------------------------- /src/parse.c: -------------------------------------------------------------------------------- 1 | #include "parse.h" 2 | 3 | #include "lex.h" 4 | 5 | #include "common.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define RULE_RHS_LAST 7 13 | #define GRAMMAR_SIZE countof(grammar) 14 | 15 | #define skip_token(t) ({ \ 16 | const lex_tk_t t_once = (t); \ 17 | t_once == LEX_TK_WSPC || t_once == LEX_TK_LCOM || t_once == LEX_TK_BCOM; \ 18 | }) 19 | 20 | static struct { 21 | size_t size, allocated; 22 | struct parse_node *nodes; 23 | } stack; 24 | 25 | struct term { 26 | /* a rule RHS term is either a terminal token or a non-terminal */ 27 | union { 28 | const lex_tk_t tk; 29 | const parse_nt_t nt; 30 | }; 31 | 32 | /* indicates which field of the above union to use */ 33 | const uint8_t is_tk: 1; 34 | 35 | /* indicates that the non-terminal can be matched multiple times */ 36 | const uint8_t is_mt: 1; 37 | }; 38 | 39 | struct rule { 40 | /* left-hand side of rule */ 41 | const parse_nt_t lhs; 42 | 43 | /* right-hand side of rule */ 44 | const struct term rhs[RULE_RHS_LAST + 1]; 45 | }; 46 | 47 | #define n(_nt) { .nt = PARSE_NT_##_nt, .is_tk = 0, .is_mt = 0 } 48 | #define m(_nt) { .nt = PARSE_NT_##_nt, .is_tk = 0, .is_mt = 1 } 49 | #define t(_tm) { .tk = LEX_TK_##_tm, .is_tk = 1, .is_mt = 0 } 50 | #define no { .tk = LEX_TK_COUNT, .is_tk = 1, .is_mt = 0 } 51 | 52 | #define r1(_lhs, t1) \ 53 | { .lhs = PARSE_NT_##_lhs, .rhs = { no, no, no, no, no, no, no, t1, } }, 54 | #define r2(_lhs, t1, t2) \ 55 | { .lhs = PARSE_NT_##_lhs, .rhs = { no, no, no, no, no, no, t1, t2, } }, 56 | #define r3(_lhs, t1, t2, t3) \ 57 | { .lhs = PARSE_NT_##_lhs, .rhs = { no, no, no, no, no, t1, t2, t3, } }, 58 | #define r4(_lhs, t1, t2, t3, t4) \ 59 | { .lhs = PARSE_NT_##_lhs, .rhs = { no, no, no, no, t1, t2, t3, t4, } }, 60 | #define r5(_lhs, t1, t2, t3, t4, t5) \ 61 | { .lhs = PARSE_NT_##_lhs, .rhs = { no, no, no, t1, t2, t3, t4, t5, } }, 62 | #define r6(_lhs, t1, t2, t3, t4, t5, t6) \ 63 | { .lhs = PARSE_NT_##_lhs, .rhs = { no, no, t1, t2, t3, t4, t5, t6, } }, 64 | #define r7(_lhs, t1, t2, t3, t4, t5, t6, t7) \ 65 | { .lhs = PARSE_NT_##_lhs, .rhs = { no, t1, t2, t3, t4, t5, t6, t7, } }, 66 | 67 | static const struct rule grammar[] = { 68 | r3(Unit, t(FBEG), m(Stmt), t(FEND) ) 69 | 70 | r5(Stmt, t(WAIT), n(Expr), t(WFOR), n(Expr), t(SCOL) ) 71 | r6(Stmt, t(WAIT), n(Expr), t(WFOR), n(Expr), t(WNOB), t(SCOL) ) 72 | r5(Stmt, t(WAIT), n(Expr), t(WUNT), n(Expr), t(SCOL) ) 73 | r6(Stmt, t(WAIT), n(Expr), t(WUNT), n(Expr), t(WNOB), t(SCOL) ) 74 | r3(Stmt, t(WAIT), n(Expr), t(SCOL) ) 75 | r4(Stmt, t(WAIT), n(Expr), t(WNOB), t(SCOL) ) 76 | r3(Stmt, t(RETN), n(Expr), t(SCOL) ) 77 | r2(Stmt, t(RETN), t(SCOL) ) 78 | r3(Stmt, t(USEU), n(Expr), t(SCOL) ) 79 | r4(Stmt, t(EXPO), t(TYPE), n(Expr), t(SCOL) ) 80 | r3(Stmt, t(TYPE), n(Expr), t(SCOL) ) 81 | 82 | r2(Ctrl, n(Cond), m(Elif) ) 83 | r3(Ctrl, n(Cond), m(Elif), n(Else) ) 84 | r1(Ctrl, n(Dowh) ) 85 | r1(Ctrl, n(Whil) ) 86 | 87 | r5(Cond, t(COND), n(Expr), t(LBRC), m(Stmt), t(RBRC) ) 88 | r5(Elif, t(ELIF), n(Expr), t(LBRC), m(Stmt), t(RBRC) ) 89 | r4(Else, t(ELSE), t(LBRC), m(Stmt), t(RBRC) ) 90 | 91 | r7(Dowh, t(DOWH), t(LBRC), m(Stmt), t(RBRC), t(WHIL), n(Expr), t(SCOL) ) 92 | r5(Whil, t(WHIL), n(Expr), t(LBRC), m(Stmt), t(RBRC) ) 93 | 94 | r3(Stmt, m(Qual), n(Expr), t(SCOL) ) 95 | r1(Stmt, n(Ctrl) ) 96 | r1(Stmt, n(Func) ) 97 | 98 | r5(Func, m(Qual), n(Expr), t(LBRC), m(Stmt), t(RBRC) ) 99 | 100 | r4(Stmt, t(NOIN), t(LBRC), m(Stmt), t(RBRC) ) 101 | r3(Stmt, t(LBRC), m(Stmt), t(RBRC) ) 102 | 103 | r1(Qual, t(CONS) ) 104 | r1(Qual, t(EXPO) ) 105 | r1(Qual, t(STAT) ) 106 | 107 | r1(Atom, t(NAME) ) 108 | r1(Atom, t(NMBR) ) 109 | r1(Atom, t(STRL) ) 110 | 111 | r1(Expr, n(Atom) ) 112 | r1(Expr, n(Fexp) ) 113 | r1(Expr, n(Pexp) ) 114 | r1(Expr, n(Texp) ) 115 | r1(Expr, n(Bexp) ) 116 | r1(Expr, n(Uexp) ) 117 | r1(Expr, n(Xexp) ) 118 | r1(Expr, n(Wexp) ) 119 | r1(Expr, n(Aexp) ) 120 | 121 | r4(Fexp, n(Expr), t(LPAR), n(Expr), t(RPAR) ) 122 | r3(Fexp, n(Expr), t(LPAR), t(RPAR) ) 123 | 124 | r3(Pexp, t(LPAR), n(Expr), t(RPAR) ) 125 | 126 | r5(Texp, n(Expr), t(QUES), n(Expr), t(COLN), n(Expr) ) 127 | 128 | r3(Bexp, n(Expr), t(ASSN), n(Expr) ) 129 | r3(Bexp, n(Expr), t(ASPL), n(Expr) ) 130 | r3(Bexp, n(Expr), t(ASMI), n(Expr) ) 131 | r3(Bexp, n(Expr), t(ASMU), n(Expr) ) 132 | r3(Bexp, n(Expr), t(ASDI), n(Expr) ) 133 | r3(Bexp, n(Expr), t(ASMO), n(Expr) ) 134 | r3(Bexp, n(Expr), t(ASLS), n(Expr) ) 135 | r3(Bexp, n(Expr), t(ASRS), n(Expr) ) 136 | r3(Bexp, n(Expr), t(ASAN), n(Expr) ) 137 | r3(Bexp, n(Expr), t(ASXO), n(Expr) ) 138 | r3(Bexp, n(Expr), t(ASOR), n(Expr) ) 139 | r3(Bexp, n(Expr), t(COLN), n(Expr) ) 140 | r3(Bexp, n(Expr), t(SCOP), n(Expr) ) 141 | r3(Bexp, n(Expr), t(ATSI), n(Expr) ) 142 | r3(Bexp, n(Expr), t(MEMB), n(Expr) ) 143 | r3(Bexp, n(Expr), t(AROW), n(Expr) ) 144 | r3(Bexp, n(Expr), t(EQUL), n(Expr) ) 145 | r3(Bexp, n(Expr), t(NEQL), n(Expr) ) 146 | r3(Bexp, n(Expr), t(LTHN), n(Expr) ) 147 | r3(Bexp, n(Expr), t(GTHN), n(Expr) ) 148 | r3(Bexp, n(Expr), t(LTEQ), n(Expr) ) 149 | r3(Bexp, n(Expr), t(GTEQ), n(Expr) ) 150 | r3(Bexp, n(Expr), t(CONJ), n(Expr) ) 151 | r3(Bexp, n(Expr), t(DISJ), n(Expr) ) 152 | r3(Bexp, n(Expr), t(PLUS), n(Expr) ) 153 | r3(Bexp, n(Expr), t(MINS), n(Expr) ) 154 | r3(Bexp, n(Expr), t(MULT), n(Expr) ) 155 | r3(Bexp, n(Expr), t(DIVI), n(Expr) ) 156 | r3(Bexp, n(Expr), t(MODU), n(Expr) ) 157 | r3(Bexp, n(Expr), t(LSHF), n(Expr) ) 158 | r3(Bexp, n(Expr), t(RSHF), n(Expr) ) 159 | r3(Bexp, n(Expr), t(AMPS), n(Expr) ) 160 | r3(Bexp, n(Expr), t(CARE), n(Expr) ) 161 | r3(Bexp, n(Expr), t(PIPE), n(Expr) ) 162 | r3(Bexp, n(Expr), t(COMA), n(Expr) ) 163 | r3(Bexp, n(Expr), t(CAST), n(Expr) ) 164 | 165 | r2(Uexp, t(PLUS), n(Expr) ) 166 | r2(Uexp, t(MINS), n(Expr) ) 167 | r2(Uexp, t(EXCL), n(Expr) ) 168 | r2(Uexp, t(TILD), n(Expr) ) 169 | r2(Uexp, t(MULT), n(Expr) ) 170 | r2(Uexp, t(AMPS), n(Expr) ) 171 | r2(Uexp, t(CARE), n(Expr) ) 172 | r2(Uexp, t(INCR), n(Expr) ) 173 | r2(Uexp, t(DECR), n(Expr) ) 174 | r2(Uexp, t(SZOF), n(Expr) ) 175 | r2(Uexp, t(ALOF), n(Expr) ) 176 | 177 | r2(Xexp, n(Expr), t(INCR) ) 178 | r2(Xexp, n(Expr), t(DECR) ) 179 | 180 | r2(Wexp, n(Expr), t(WMSE) ) 181 | r2(Wexp, n(Expr), t(WSEC) ) 182 | 183 | r4(Aexp, n(Expr), t(LBRA), n(Expr), t(RBRA) ) 184 | 185 | r3(Stmt, t(LBRA), n(Expr), t(RBRA) ) 186 | }; 187 | 188 | #undef r1 189 | #undef r2 190 | #undef r3 191 | #undef r4 192 | #undef r5 193 | #undef r6 194 | #undef r7 195 | 196 | #undef n 197 | #undef m 198 | #undef t 199 | #undef no 200 | 201 | static const uint8_t precedence[] = { 202 | /* = */ 14, 203 | /* += */ 14, 204 | /* -= */ 14, 205 | /* *= */ 14, 206 | /* /= */ 14, 207 | /* %= */ 14, 208 | /* <<= */ 14, 209 | /* >>= */ 14, 210 | /* &= */ 14, 211 | /* ^= */ 14, 212 | /* |= */ 14, 213 | /* : */ 1, 214 | /* :: */ 0, 215 | /* @ */ 2, 216 | /* . */ 1, 217 | /* -> */ 1, 218 | /* == */ 7, 219 | /* != */ 7, 220 | /* < */ 6, 221 | /* > */ 6, 222 | /* <= */ 6, 223 | /* >= */ 6, 224 | /* && */ 11, 225 | /* || */ 12, 226 | /* + */ 4, 227 | /* - */ 4, 228 | /* * */ 3, 229 | /* / */ 3, 230 | /* % */ 3, 231 | /* << */ 5, 232 | /* >> */ 5, 233 | /* & */ 8, 234 | /* ^ */ 9, 235 | /* | */ 10, 236 | /* , */ 15, 237 | /* as */ 2, 238 | /* ?: */ 13, 239 | }; 240 | 241 | static_assert(sizeof(precedence) == LEX_TK_QUES - LEX_TK_ASSN + 1, "mismatch"); 242 | 243 | static void destroy_node(const struct parse_node *const node) 244 | { 245 | if (likely(node->nchildren)) { 246 | for (size_t child_idx = 0; child_idx < node->nchildren; ++child_idx) { 247 | destroy_node(node->children[child_idx]); 248 | } 249 | 250 | free(node->children[0]); 251 | free(node->children); 252 | } 253 | } 254 | 255 | static void deallocate_stack(void) 256 | { 257 | free(stack.nodes); 258 | stack.nodes = NULL; 259 | stack.size = 0; 260 | stack.allocated = 0; 261 | } 262 | 263 | static void destroy_stack(void) 264 | { 265 | for (size_t node_idx = 0; node_idx < stack.size; ++node_idx) { 266 | destroy_node(&stack.nodes[node_idx]); 267 | } 268 | 269 | deallocate_stack(); 270 | } 271 | 272 | static inline bool term_eq_node(const struct term *const term, 273 | const struct parse_node *const node) 274 | { 275 | const bool node_is_leaf = node->nchildren == 0; 276 | 277 | if (term->is_tk == node_is_leaf) { 278 | if (node_is_leaf) { 279 | return term->tk == node->token->tk; 280 | } else { 281 | return term->nt == node->nt; 282 | } 283 | } 284 | 285 | return false; 286 | } 287 | 288 | static size_t match_rule(const struct rule *const rule, size_t *const at) 289 | { 290 | const struct term *prev = NULL; 291 | const struct term *term = &rule->rhs[RULE_RHS_LAST]; 292 | ssize_t st_idx = (ssize_t) stack.size - 1; 293 | 294 | do { 295 | if (term_eq_node(term, &stack.nodes[st_idx])) { 296 | prev = term->is_mt ? term : NULL; 297 | --term, --st_idx; 298 | } else if (prev && term_eq_node(prev, &stack.nodes[st_idx])) { 299 | --st_idx; 300 | } else if (term->is_mt) { 301 | prev = NULL; 302 | --term; 303 | } else { 304 | prev = NULL; 305 | term = NULL; 306 | break; 307 | } 308 | } while (st_idx >= 0 && !(term->is_tk && term->tk == LEX_TK_COUNT)); 309 | 310 | const bool reached_eor = term && term->is_tk && term->tk == LEX_TK_COUNT; 311 | 312 | if (reached_eor) { 313 | if (prev) { 314 | while (st_idx >= 0 && term_eq_node(prev, &stack.nodes[st_idx])) { 315 | --st_idx; 316 | } 317 | } 318 | 319 | const size_t reduction_size = stack.size - (size_t) st_idx - 1; 320 | return *at = (size_t) st_idx + 1, reduction_size; 321 | } else { 322 | return 0; 323 | } 324 | } 325 | 326 | static inline int shift(const struct lex_token *const token) 327 | { 328 | if (unlikely(stack.size >= stack.allocated)) { 329 | stack.allocated = (stack.allocated ?: 1) * 8; 330 | 331 | struct parse_node *const tmp = realloc(stack.nodes, 332 | stack.allocated * sizeof(struct parse_node)); 333 | 334 | if (unlikely(!tmp)) { 335 | return PARSE_NOMEM; 336 | } 337 | 338 | stack.nodes = tmp; 339 | } 340 | 341 | stack.nodes[stack.size++] = (struct parse_node) { 342 | .nchildren = 0, 343 | .token = token, 344 | }; 345 | 346 | return PARSE_OK; 347 | } 348 | 349 | static inline bool should_shift_pre(const struct rule *const rule, 350 | const struct lex_token *const tokens, size_t *const token_idx) 351 | { 352 | if (unlikely(rule->lhs == PARSE_NT_Unit)) { 353 | return false; 354 | } 355 | 356 | while (skip_token(tokens[*token_idx].tk)) { 357 | ++*token_idx; 358 | } 359 | 360 | const lex_tk_t ahead = tokens[*token_idx].tk; 361 | 362 | if (rule->lhs == PARSE_NT_Bexp) { 363 | const lex_tk_t op = rule->rhs[RULE_RHS_LAST - 1].tk; 364 | 365 | if (ahead >= LEX_TK_ASSN && ahead <= LEX_TK_QUES) { 366 | const uint8_t p1 = precedence[op - LEX_TK_ASSN]; 367 | const uint8_t p2 = precedence[ahead - LEX_TK_ASSN]; 368 | 369 | if (p1 > p2) { 370 | return true; 371 | } 372 | 373 | if (p1 == p2) { 374 | return lex_tk_is_assn(op) || 375 | op == LEX_TK_COLN || op == LEX_TK_COMA || op == LEX_TK_SCOP; 376 | } 377 | } else if (ahead == LEX_TK_LPAR || ahead == LEX_TK_LBRA || 378 | ahead == LEX_TK_INCR || ahead == LEX_TK_DECR) { 379 | 380 | switch (op) { 381 | case LEX_TK_SCOP: 382 | case LEX_TK_MEMB: 383 | case LEX_TK_AROW: 384 | return false; 385 | 386 | default: 387 | return true; 388 | } 389 | } 390 | } else if (rule->lhs == PARSE_NT_Uexp) { 391 | switch (ahead) { 392 | case LEX_TK_LPAR: 393 | case LEX_TK_LBRA: 394 | case LEX_TK_SCOP: 395 | case LEX_TK_COLN: 396 | case LEX_TK_MEMB: 397 | case LEX_TK_AROW: 398 | case LEX_TK_ATSI: 399 | case LEX_TK_INCR: 400 | case LEX_TK_DECR: 401 | return true; 402 | } 403 | } else if (rule->lhs == PARSE_NT_Texp) { 404 | if (lex_tk_is_assn(ahead) || ahead == LEX_TK_COMA || 405 | ahead == LEX_TK_RPAR || ahead == LEX_TK_RBRA || ahead == LEX_TK_SCOL) { 406 | 407 | return false; 408 | } else { 409 | return true; 410 | } 411 | } else if (rule->lhs == PARSE_NT_Stmt && rule->rhs[RULE_RHS_LAST].is_tk && 412 | rule->rhs[RULE_RHS_LAST].tk == LEX_TK_RBRC) { 413 | 414 | /* fixme: { } while 0 { } causes a parse error: check whether "do" exists */ 415 | if (ahead == LEX_TK_WHIL) { 416 | return true; 417 | } 418 | } else if (rule->lhs == PARSE_NT_Qual) { 419 | if (rule->rhs[RULE_RHS_LAST].tk == LEX_TK_EXPO && ahead == LEX_TK_TYPE) { 420 | return true; 421 | } 422 | } 423 | 424 | return false; 425 | } 426 | 427 | static inline bool should_shift_post(const struct rule *const rule, 428 | const struct lex_token *const tokens, size_t *const token_idx) 429 | { 430 | if (unlikely(rule->lhs == PARSE_NT_Unit)) { 431 | return false; 432 | } 433 | 434 | while (skip_token(tokens[*token_idx].tk)) { 435 | ++*token_idx; 436 | } 437 | 438 | if (rule->lhs == PARSE_NT_Cond || rule->lhs == PARSE_NT_Elif) { 439 | const lex_tk_t ahead = tokens[*token_idx].tk; 440 | 441 | if (ahead == LEX_TK_ELIF || ahead == LEX_TK_ELSE) { 442 | return true; 443 | } 444 | } 445 | 446 | return false; 447 | } 448 | 449 | static int reduce(const struct rule *const rule, 450 | const size_t at, const size_t size) 451 | { 452 | struct parse_node *const child_nodes = 453 | malloc(size * sizeof(struct parse_node)); 454 | 455 | if (unlikely(!child_nodes)) { 456 | return PARSE_NOMEM; 457 | } 458 | 459 | struct parse_node *const reduce_at = &stack.nodes[at]; 460 | struct parse_node **const old_children = reduce_at->children; 461 | reduce_at->children = malloc(size * sizeof(struct node *)) ?: old_children; 462 | 463 | if (unlikely(reduce_at->children == old_children)) { 464 | return free(child_nodes), PARSE_NOMEM; 465 | } 466 | 467 | for (size_t child_idx = 0, st_idx = at; 468 | st_idx < stack.size; 469 | ++st_idx, ++child_idx) { 470 | 471 | child_nodes[child_idx] = stack.nodes[st_idx]; 472 | reduce_at->children[child_idx] = &child_nodes[child_idx]; 473 | } 474 | 475 | child_nodes[0].children = old_children; 476 | reduce_at->nchildren = size; 477 | reduce_at->nt = rule->lhs; 478 | stack.size = at + 1; 479 | return PARSE_OK; 480 | } 481 | 482 | static void print_localised_parse_error(FILE *const out, 483 | const struct parse_node *node) 484 | { 485 | for (; node->nchildren; node = node->children[0]); 486 | 487 | size_t line, col; 488 | lex_locate_linecol(node->token, &line, &col); 489 | 490 | fprintf(out, "%s:%zu:%zu: parse error, unexpected ", 491 | lex_current_file, line, col); 492 | 493 | const struct lex_token *const tok = node->token; 494 | 495 | if (unlikely(tok->tk == LEX_TK_FEND)) { 496 | fprintf(out, "end of file\n"); 497 | } else { 498 | fprintf(out, RED("%.*s") "\n", (int) (tok->end - tok->beg), tok->beg); 499 | } 500 | } 501 | 502 | static void diagnose_error(void) 503 | { 504 | struct status { 505 | const struct term *prev, *beg, *pos, *end; 506 | size_t prefix_len; 507 | bool accepted; 508 | }; 509 | 510 | struct status statuses[GRAMMAR_SIZE]; 511 | 512 | for (size_t rule_idx = 0; rule_idx < GRAMMAR_SIZE; ++rule_idx) { 513 | const struct term *beg = grammar[rule_idx].rhs; 514 | 515 | while (++beg && beg->is_tk && beg->tk == LEX_TK_COUNT); 516 | 517 | statuses[rule_idx] = (struct status) { 518 | .prev = NULL, 519 | .beg = beg, 520 | .pos = beg, 521 | .end = grammar[rule_idx].rhs + RULE_RHS_LAST + 1, 522 | .prefix_len = 0, 523 | .accepted = true, 524 | }; 525 | } 526 | 527 | size_t st_idx = 0; 528 | 529 | do { 530 | const struct parse_node *const node = &stack.nodes[st_idx]; 531 | bool did_accept = false; 532 | 533 | for (size_t rule_idx = 0; rule_idx < GRAMMAR_SIZE; ++rule_idx) { 534 | struct status *const status = &statuses[rule_idx]; 535 | 536 | if (status->accepted) { 537 | match_again: 538 | if (term_eq_node(status->pos, node)) { 539 | did_accept = true; 540 | status->prev = status->pos->is_mt ? status->pos : NULL; 541 | status->pos++; 542 | status->prefix_len++; 543 | } else if (status->prev && term_eq_node(status->prev, node)) { 544 | did_accept = true; 545 | status->prefix_len++; 546 | } else if (status->pos->is_mt) { 547 | status->prev = NULL; 548 | status->pos++; 549 | 550 | if (status->pos != status->end) { 551 | goto match_again; 552 | } 553 | } else { 554 | status->accepted = false; 555 | } 556 | } 557 | } 558 | 559 | if (!did_accept) { 560 | bool all_unmatched = true; 561 | 562 | for (size_t rule_idx = 0; rule_idx < GRAMMAR_SIZE; ++rule_idx) { 563 | if (statuses[rule_idx].prefix_len) { 564 | all_unmatched = false; 565 | break; 566 | } 567 | } 568 | 569 | if (all_unmatched) { 570 | print_localised_parse_error(stderr, node); 571 | break; 572 | } else { 573 | --st_idx; 574 | 575 | for (size_t rule_idx = 0; rule_idx < GRAMMAR_SIZE; ++rule_idx) { 576 | statuses[rule_idx].prev = NULL; 577 | statuses[rule_idx].pos = statuses[rule_idx].beg; 578 | statuses[rule_idx].prefix_len = 0; 579 | statuses[rule_idx].accepted = true; 580 | } 581 | } 582 | } 583 | } while (++st_idx < stack.size); 584 | } 585 | 586 | struct parse_node parse(const struct lex_token *const tokens, const size_t ntokens) 587 | { 588 | static const struct lex_token 589 | reject = { .tk = PARSE_REJECT }, 590 | nomem = { .tk = PARSE_NOMEM }; 591 | 592 | static const struct parse_node 593 | err_reject = { .nchildren = 0, .token = &reject }, 594 | err_nomem = { .nchildren = 0, .token = &nomem }; 595 | 596 | #define SHIFT_OR_NOMEM(t) \ 597 | if (unlikely(shift(t))) { \ 598 | fprintf(stderr, "out of memory while parsing (shift)\n"); \ 599 | return destroy_stack(), err_nomem; \ 600 | } 601 | 602 | #define REDUCE_OR_NOMEM(r, a, s) \ 603 | if (unlikely(reduce(r, a, s))) { \ 604 | fprintf(stderr, "out of memory while parsing (reduce)\n"); \ 605 | return destroy_stack(), err_nomem; \ 606 | } 607 | 608 | for (size_t token_idx = 0; token_idx < ntokens; ) { 609 | if (skip_token(tokens[token_idx].tk)) { 610 | ++token_idx; 611 | continue; 612 | } 613 | 614 | SHIFT_OR_NOMEM(&tokens[token_idx++]); 615 | 616 | try_reduce_again:; 617 | const struct rule *rule = grammar; 618 | 619 | do { 620 | size_t reduction_at, reduction_size; 621 | 622 | if ((reduction_size = match_rule(rule, &reduction_at))) { 623 | const bool do_shift = should_shift_pre(rule, tokens, &token_idx); 624 | 625 | if (!do_shift) { 626 | REDUCE_OR_NOMEM(rule, reduction_at, reduction_size); 627 | } 628 | 629 | if (do_shift || should_shift_post(rule, tokens, &token_idx)) { 630 | SHIFT_OR_NOMEM(&tokens[token_idx++]); 631 | } 632 | 633 | goto try_reduce_again; 634 | } 635 | } while (++rule != grammar + GRAMMAR_SIZE); 636 | } 637 | 638 | #undef SHIFT_OR_NOMEM 639 | #undef REDUCE_OR_NOMEM 640 | 641 | const bool accepted = stack.size == 1 && 642 | stack.nodes[0].nchildren && stack.nodes[0].nt == PARSE_NT_Unit; 643 | 644 | if (accepted) { 645 | const struct parse_node ret = stack.nodes[0]; 646 | return deallocate_stack(), ret; 647 | } else { 648 | return diagnose_error(), destroy_stack(), err_reject; 649 | } 650 | } 651 | 652 | void parse_tree_destroy(const struct parse_node root) 653 | { 654 | destroy_node(&root); 655 | } 656 | 657 | void parse_node_ltok_rtok(const struct parse_node *const node, 658 | const struct lex_token **const ltok, const struct lex_token **const rtok) 659 | { 660 | const struct parse_node *lhs = node, *rhs = node; 661 | 662 | for (; lhs->nchildren; lhs = lhs->children[0]); 663 | for (; rhs->nchildren; rhs = rhs->children[rhs->nchildren - 1]); 664 | 665 | *ltok = lhs->token, *rtok = rhs->token; 666 | } 667 | -------------------------------------------------------------------------------- /src/parse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct lex_token; 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | enum { 10 | PARSE_NT_Unit, 11 | PARSE_NT_Stmt, 12 | PARSE_NT_Ctrl, 13 | PARSE_NT_Cond, 14 | PARSE_NT_Elif, 15 | PARSE_NT_Else, 16 | PARSE_NT_Dowh, 17 | PARSE_NT_Whil, 18 | PARSE_NT_Func, 19 | PARSE_NT_Qual, 20 | PARSE_NT_Atom, 21 | PARSE_NT_Expr, 22 | PARSE_NT_Fexp, 23 | PARSE_NT_Pexp, 24 | PARSE_NT_Texp, 25 | PARSE_NT_Bexp, 26 | PARSE_NT_Uexp, 27 | PARSE_NT_Xexp, 28 | PARSE_NT_Wexp, 29 | PARSE_NT_Aexp, 30 | PARSE_NT_COUNT 31 | }; 32 | 33 | static_assert(PARSE_NT_COUNT - 1 <= UINT8_MAX, ""); 34 | typedef uint8_t parse_nt_t; 35 | 36 | struct parse_node { 37 | size_t nchildren; 38 | 39 | union { 40 | /* nchildren == 0 */ 41 | const struct lex_token *token; 42 | 43 | /* nchildren != 0 */ 44 | struct { 45 | parse_nt_t nt; 46 | struct parse_node **children; 47 | }; 48 | }; 49 | }; 50 | 51 | #define parse_node_is_tk(node) ((node)->nchildren == 0) 52 | #define parse_node_tk(node) ((node)->token->tk) 53 | #define parse_node_is_nt(node) ((node)->nchildren != 0) 54 | #define parse_node_nt(node) ((node)->nt) 55 | 56 | struct parse_node parse(const struct lex_token *, size_t); 57 | 58 | enum { 59 | PARSE_OK = 0, 60 | PARSE_NOMEM, 61 | PARSE_REJECT, 62 | }; 63 | 64 | #define parse_error(root) ({ \ 65 | struct parse_node root_once = (root); \ 66 | root_once.nchildren ? PARSE_OK : root_once.token->tk; \ 67 | }) 68 | 69 | void parse_tree_destroy(struct parse_node); 70 | void parse_node_ltok_rtok(const struct parse_node *, 71 | const struct lex_token **, const struct lex_token **); 72 | -------------------------------------------------------------------------------- /src/scope.c: -------------------------------------------------------------------------------- 1 | #include "scope.h" 2 | 3 | #include "lex.h" 4 | #include "ast.h" 5 | #include "type.h" 6 | 7 | #include "common.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define scope_alloc(objcount) \ 16 | calloc(1, sizeof(struct scope) + (objcount) * sizeof(struct scope_obj)) 17 | 18 | #define NOMEM \ 19 | (fprintf(stderr, "%s:%d: no memory\n", __FILE__, __LINE__), SCOPE_NOMEM) 20 | 21 | static inline void aggr_error(int *const error, const int current) 22 | { 23 | assert(*error >= SCOPE_OK && *error <= SCOPE_DUPLICATED); 24 | assert(current >= SCOPE_OK && current <= SCOPE_DUPLICATED); 25 | 26 | switch (*error) { 27 | case SCOPE_OK: 28 | *error = current; 29 | break; 30 | 31 | case SCOPE_NOMEM: 32 | break; 33 | 34 | case SCOPE_DUPLICATED: 35 | *error = current == SCOPE_NOMEM ? SCOPE_NOMEM : SCOPE_DUPLICATED; 36 | break; 37 | } 38 | } 39 | 40 | const struct scope_builtin_const scope_builtin_consts[] = { 41 | { 42 | lex_sym("null"), 43 | type(VPTR, 1), 44 | }, 45 | 46 | { 47 | lex_sym("true"), 48 | type(U8, 1), 49 | }, 50 | 51 | { 52 | lex_sym("false"), 53 | type(U8, 1), 54 | }, 55 | }; 56 | 57 | static_assert(countof(scope_builtin_consts) == SCOPE_BCON_ID_COUNT, ""); 58 | 59 | #define PARAMS(...) (const struct type_nt_pair []) { \ 60 | __VA_ARGS__ \ 61 | } 62 | 63 | const struct scope_builtin_func scope_builtin_funcs[] = { 64 | { 65 | .name = lex_sym("|"), 66 | .rettype = NULL, 67 | .param_count = 0, 68 | .params = NULL, 69 | }, 70 | 71 | { 72 | .name = lex_sym("monotime"), 73 | .rettype = type(U64, 1), 74 | .param_count = 0, 75 | .params = NULL, 76 | }, 77 | 78 | { 79 | .name = lex_sym("malloc"), 80 | .rettype = type(VPTR, 1), 81 | .param_count = 1, 82 | .params = PARAMS 83 | ( 84 | { 85 | .name = lex_sym("size"), 86 | .type = type(USIZE, 1), 87 | } 88 | ) 89 | }, 90 | 91 | { 92 | .name = lex_sym("calloc"), 93 | .rettype = type(VPTR, 1), 94 | .param_count = 1, 95 | .params = PARAMS 96 | ( 97 | { 98 | .name = lex_sym("size"), 99 | .type = type(USIZE, 1), 100 | } 101 | ) 102 | }, 103 | 104 | { 105 | .name = lex_sym("realloc"), 106 | .rettype = type(VPTR, 1), 107 | .param_count = 2, 108 | .params = PARAMS 109 | ( 110 | { 111 | .name = lex_sym("oldptr"), 112 | .type = type(VPTR, 1), 113 | }, 114 | { 115 | .name = lex_sym("newsize"), 116 | .type = type(USIZE, 1), 117 | } 118 | ) 119 | }, 120 | 121 | { 122 | .name = lex_sym("free"), 123 | .rettype = NULL, 124 | .param_count = 1, 125 | .params = PARAMS 126 | ( 127 | { 128 | .name = lex_sym("ptr"), 129 | .type = type(VPTR, 1), 130 | } 131 | ) 132 | }, 133 | 134 | { 135 | .name = lex_sym("ps"), 136 | .rettype = NULL, 137 | .param_count = 1, 138 | .params = PARAMS 139 | ( 140 | { 141 | .name = lex_sym("str"), 142 | .type = type_ptr(1, type(U8, 1)), 143 | } 144 | ) 145 | }, 146 | 147 | { 148 | .name = lex_sym("pu8"), 149 | .rettype = NULL, 150 | .param_count = 1, 151 | .params = PARAMS 152 | ( 153 | { 154 | .name = lex_sym("num"), 155 | .type = type(U8, 1), 156 | } 157 | ) 158 | }, 159 | 160 | { 161 | .name = lex_sym("pi8"), 162 | .rettype = NULL, 163 | .param_count = 1, 164 | .params = PARAMS 165 | ( 166 | { 167 | .name = lex_sym("num"), 168 | .type = type(I8, 1), 169 | } 170 | ) 171 | }, 172 | 173 | { 174 | .name = lex_sym("pu16"), 175 | .rettype = NULL, 176 | .param_count = 1, 177 | .params = PARAMS 178 | ( 179 | { 180 | .name = lex_sym("num"), 181 | .type = type(U16, 1), 182 | } 183 | ) 184 | }, 185 | 186 | { 187 | .name = lex_sym("pi16"), 188 | .rettype = NULL, 189 | .param_count = 1, 190 | .params = PARAMS 191 | ( 192 | { 193 | .name = lex_sym("num"), 194 | .type = type(I16, 1), 195 | } 196 | ) 197 | }, 198 | 199 | { 200 | .name = lex_sym("pu32"), 201 | .rettype = NULL, 202 | .param_count = 1, 203 | .params = PARAMS 204 | ( 205 | { 206 | .name = lex_sym("num"), 207 | .type = type(U32, 1), 208 | } 209 | ) 210 | }, 211 | 212 | { 213 | .name = lex_sym("pi32"), 214 | .rettype = NULL, 215 | .param_count = 1, 216 | .params = PARAMS 217 | ( 218 | { 219 | .name = lex_sym("num"), 220 | .type = type(I32, 1), 221 | } 222 | ) 223 | }, 224 | 225 | { 226 | .name = lex_sym("pu64"), 227 | .rettype = NULL, 228 | .param_count = 1, 229 | .params = PARAMS 230 | ( 231 | { 232 | .name = lex_sym("num"), 233 | .type = type(U64, 1), 234 | } 235 | ) 236 | }, 237 | 238 | { 239 | .name = lex_sym("pi64"), 240 | .rettype = NULL, 241 | .param_count = 1, 242 | .params = PARAMS 243 | ( 244 | { 245 | .name = lex_sym("num"), 246 | .type = type(I64, 1), 247 | } 248 | ) 249 | }, 250 | 251 | { 252 | .name = lex_sym("pnl"), 253 | .rettype = NULL, 254 | .param_count = 0, 255 | .params = NULL 256 | }, 257 | 258 | { 259 | .name = lex_sym("exit"), 260 | .rettype = NULL, 261 | .param_count = 1, 262 | .params = PARAMS 263 | ( 264 | { 265 | .name = lex_sym("status"), 266 | .type = type(I32, 1), 267 | } 268 | ) 269 | }, 270 | }; 271 | 272 | #undef PARAMS 273 | static_assert(countof(scope_builtin_funcs) == SCOPE_BFUN_ID_COUNT, "bfun mismatch"); 274 | 275 | static int cmp_scope_obj(const void *const a, const void *const b) 276 | { 277 | const struct scope_obj *const s1 = a; 278 | const struct scope_obj *const s2 = b; 279 | 280 | const size_t len1 = (size_t) (s1->name->end - s1->name->beg); 281 | const size_t len2 = (size_t) (s2->name->end - s2->name->beg); 282 | 283 | char str1[len1 + 1]; 284 | char str2[len2 + 1]; 285 | 286 | str1[len1] = '\0'; 287 | str2[len2] = '\0'; 288 | 289 | strncpy(str1, (const char *) s1->name->beg, len1); 290 | strncpy(str2, (const char *) s2->name->beg, len2); 291 | 292 | return strcmp(str1, str2); 293 | } 294 | 295 | static int add_func_wlab(struct ast_func *const func, 296 | const struct ast_node *const node) 297 | { 298 | const struct ast_wlab *const wlab = ast_data(node, wlab); 299 | 300 | func->wlab_count++; 301 | void *const tmp = realloc(func->wlabs, sizeof(*func->wlabs) * func->wlab_count); 302 | 303 | if (unlikely(!tmp)) { 304 | return func->wlab_count = 0, free(func->wlabs), func->wlabs = NULL, NOMEM; 305 | } 306 | 307 | func->wlabs = tmp; 308 | func->wlabs[func->wlab_count - 1].name = wlab->name; 309 | 310 | return SCOPE_OK; 311 | } 312 | 313 | static inline void identify_wlabs(const struct ast_func *const func) 314 | { 315 | uint64_t wlab_id = 1; 316 | 317 | for (size_t idx = 0; idx < func->wlab_count; wlab_id++) { 318 | const struct lex_symbol *const outer_name = func->wlabs[idx].name; 319 | func->wlabs[idx++].id = wlab_id; 320 | 321 | while (idx < func->wlab_count && 322 | lex_symbols_equal(outer_name, func->wlabs[idx].name)) { 323 | 324 | func->wlabs[idx++].id = wlab_id; 325 | } 326 | } 327 | } 328 | 329 | static inline size_t count_objects_unit(const struct ast_unit *const unit) 330 | { 331 | size_t objcount = SCOPE_BCON_ID_COUNT + SCOPE_BFUN_ID_COUNT; 332 | 333 | for (size_t idx = 0; idx < unit->stmt_count; ++idx) { 334 | const struct ast_node *const stmt = unit->stmts[idx]; 335 | 336 | if (unlikely(!stmt)) { 337 | continue; 338 | } 339 | 340 | if (stmt->an == AST_AN_DECL) { 341 | objcount += ast_data(stmt, decl)->name_count; 342 | } else if (stmt->an == AST_AN_FUNC) { 343 | objcount++; 344 | } 345 | } 346 | 347 | return objcount; 348 | } 349 | 350 | static size_t count_objects_block(const struct ast_node *const node) 351 | { 352 | struct ast_node *const *stmts; 353 | size_t stmt_count; 354 | size_t objcount = 0; 355 | 356 | switch (node->an) { 357 | case AST_AN_BLOK: 358 | case AST_AN_NOIN: { 359 | const struct ast_blok *const blok = ast_data(node, blok); 360 | stmts = blok->stmts, stmt_count = blok->stmt_count; 361 | } break; 362 | 363 | case AST_AN_WHIL: 364 | case AST_AN_DOWH: { 365 | const struct ast_whil *const whil = ast_data(node, whil); 366 | stmts = whil->stmts, stmt_count = whil->stmt_count; 367 | } break; 368 | 369 | case AST_AN_FUNC: { 370 | const struct ast_func *const func = ast_data(node, func); 371 | stmts = func->stmts, stmt_count = func->stmt_count; 372 | objcount += func->param_count; 373 | } break; 374 | 375 | default: assert(0), abort(); 376 | } 377 | 378 | for (size_t idx = 0; idx < stmt_count; ++idx) { 379 | if (stmts[idx] && stmts[idx]->an == AST_AN_DECL) { 380 | objcount += ast_data(stmts[idx], decl)->name_count; 381 | } 382 | } 383 | 384 | return objcount; 385 | } 386 | 387 | static int find_duplicates(struct scope_obj *const scope_objs, 388 | const size_t objcount) 389 | { 390 | bool outer_dup = false; 391 | 392 | for (size_t outer_idx = 0; outer_idx < objcount; ++outer_idx) { 393 | struct scope_obj *const so_outer = &scope_objs[outer_idx]; 394 | 395 | if (so_outer->obj == SCOPE_OBJ_DUPL) { 396 | continue; 397 | } 398 | 399 | bool inner_dup = false; 400 | 401 | for (size_t inner_idx = outer_idx + 1; inner_idx < objcount; ++inner_idx) { 402 | struct scope_obj *const so_inner = &scope_objs[inner_idx]; 403 | 404 | if (unlikely(lex_symbols_equal(so_outer->name, so_inner->name))) { 405 | outer_dup = inner_dup = true; 406 | 407 | const struct lex_token *const tok = 408 | (const struct lex_token *) (so_inner)->name; 409 | 410 | lex_print_error(stderr, "duplicate declaration", tok, tok); 411 | so_inner->obj = SCOPE_OBJ_DUPL; 412 | } 413 | } 414 | 415 | if (inner_dup) { 416 | so_outer->obj = SCOPE_OBJ_DUPL; 417 | } 418 | } 419 | 420 | return outer_dup ? SCOPE_DUPLICATED : SCOPE_OK; 421 | } 422 | 423 | static inline size_t add_builtins(const struct ast_unit *const unit) 424 | { 425 | size_t offset = 0; 426 | 427 | for (scope_bcon_id_t id = 0; id < SCOPE_BCON_ID_COUNT; ++id) { 428 | unit->scope->objs[offset++] = (struct scope_obj) { 429 | .name = scope_builtin_consts[id].name, 430 | .obj = SCOPE_OBJ_BCON, 431 | .bcon_id = id, 432 | }; 433 | } 434 | 435 | for (scope_bfun_id_t id = 0; id < SCOPE_BFUN_ID_COUNT; ++id) { 436 | unit->scope->objs[offset++] = (struct scope_obj) { 437 | .name = scope_builtin_funcs[id].name, 438 | .obj = SCOPE_OBJ_BFUN, 439 | .bfun_id = id, 440 | }; 441 | } 442 | 443 | return offset; 444 | } 445 | 446 | static int scope_build_inner(struct ast_node *const node, 447 | const struct scope *const outer) 448 | { 449 | assert(node != NULL); 450 | 451 | static struct ast_func *outer_func; 452 | int error = SCOPE_OK; 453 | 454 | switch (node->an) { 455 | case AST_AN_FUNC: { 456 | struct ast_func *const func = ast_data(node, func); 457 | const size_t objcount = count_objects_block(node); 458 | 459 | assert(func->scope == NULL); 460 | 461 | if (unlikely(!(func->scope = scope_alloc(objcount)))) { 462 | return NOMEM; 463 | } 464 | 465 | func->scope->outer = outer; 466 | func->scope->objcount = objcount; 467 | size_t offset = 0; 468 | 469 | for (size_t param_idx = 0; param_idx < func->param_count; ++param_idx) { 470 | func->scope->objs[offset++] = (struct scope_obj) { 471 | .name = func->params[param_idx].name, 472 | .obj = SCOPE_OBJ_PARM, 473 | .type = func->params[param_idx].type, 474 | }; 475 | } 476 | 477 | outer_func = func; 478 | 479 | for (size_t idx = 0; idx < func->stmt_count; ++idx) { 480 | struct ast_node *const stmt = func->stmts[idx]; 481 | 482 | if (unlikely(!stmt)) { 483 | continue; 484 | } 485 | 486 | if (stmt->an == AST_AN_DECL) { 487 | const struct ast_decl *const decl = ast_data(stmt, decl); 488 | 489 | for (size_t name_idx = 0; name_idx < decl->name_count; ++name_idx) { 490 | func->scope->objs[offset++] = (struct scope_obj) { 491 | .name = decl->names[name_idx], 492 | .obj = SCOPE_OBJ_AVAR, 493 | .decl = stmt, 494 | }; 495 | } 496 | } else if (stmt->an == AST_AN_WLAB) { 497 | aggr_error(&error, add_func_wlab(func, stmt)); 498 | } else { 499 | aggr_error(&error, scope_build_inner(stmt, func->scope)); 500 | } 501 | } 502 | 503 | qsort(func->wlabs, func->wlab_count, sizeof(*func->wlabs), cmp_scope_obj); 504 | identify_wlabs(func); 505 | aggr_error(&error, find_duplicates(func->scope->objs, objcount)); 506 | qsort(func->scope->objs, objcount, sizeof(struct scope_obj), cmp_scope_obj); 507 | } break; 508 | 509 | case AST_AN_BLOK: 510 | case AST_AN_NOIN: 511 | case AST_AN_WHIL: 512 | case AST_AN_DOWH: { 513 | struct scope **scope; 514 | size_t stmt_count; 515 | struct ast_node **stmts; 516 | const size_t objcount = count_objects_block(node); 517 | 518 | if (node->an == AST_AN_BLOK || node->an == AST_AN_NOIN) { 519 | struct ast_blok *const blok = ast_data(node, blok); 520 | scope = &blok->scope; 521 | stmt_count = blok->stmt_count; 522 | stmts = blok->stmts; 523 | } else { 524 | struct ast_whil *const whil = ast_data(node, whil); 525 | scope = &whil->scope; 526 | stmt_count = whil->stmt_count; 527 | stmts = whil->stmts; 528 | } 529 | 530 | assert(*scope == NULL); 531 | 532 | if (unlikely(!(*scope = scope_alloc(objcount)))) { 533 | return NOMEM; 534 | } 535 | 536 | (*scope)->outer = outer; 537 | (*scope)->objcount = objcount; 538 | 539 | for (size_t idx = 0, offset = 0; idx < stmt_count; ++idx) { 540 | struct ast_node *const stmt = stmts[idx]; 541 | 542 | if (unlikely(!stmt)) { 543 | continue; 544 | } 545 | 546 | if (stmt->an == AST_AN_DECL) { 547 | const struct ast_decl *const decl = ast_data(stmt, decl); 548 | 549 | for (size_t name_idx = 0; name_idx < decl->name_count; ++name_idx) { 550 | (*scope)->objs[offset++] = (struct scope_obj) { 551 | .name = decl->names[name_idx], 552 | .obj = SCOPE_OBJ_AVAR, 553 | .decl = stmt, 554 | }; 555 | } 556 | } else if (stmt->an == AST_AN_WLAB) { 557 | aggr_error(&error, add_func_wlab(outer_func, stmt)); 558 | } else { 559 | aggr_error(&error, scope_build_inner(stmt, *scope)); 560 | } 561 | } 562 | 563 | aggr_error(&error, find_duplicates((*scope)->objs, objcount)); 564 | qsort((*scope)->objs, objcount, sizeof(struct scope_obj), cmp_scope_obj); 565 | } break; 566 | 567 | case AST_AN_COND: { 568 | struct ast_cond *const cond = ast_data(node, cond); 569 | 570 | aggr_error(&error, scope_build_inner(cond->if_block, outer)); 571 | 572 | for (size_t idx = 0; idx < cond->elif_count; ++idx) { 573 | aggr_error(&error, scope_build_inner(cond->elif[idx].block, outer)); 574 | } 575 | 576 | if (cond->else_block) { 577 | aggr_error(&error, scope_build_inner(cond->else_block, outer)); 578 | } 579 | } break; 580 | } 581 | 582 | return error; 583 | } 584 | 585 | int scope_build(struct ast_node *const root) 586 | { 587 | assert(root != NULL); 588 | 589 | struct ast_unit *const unit = ast_data(root, unit); 590 | size_t objcount = count_objects_unit(unit); 591 | 592 | assert(unit->scope == NULL); 593 | 594 | if (unlikely(!(unit->scope = scope_alloc(objcount)))) { 595 | return NOMEM; 596 | } 597 | 598 | unit->scope->objcount = objcount; 599 | size_t offset = add_builtins(unit); 600 | int error = SCOPE_OK; 601 | 602 | for (size_t idx = 0; idx < unit->stmt_count; ++idx) { 603 | struct ast_node *const stmt = unit->stmts[idx]; 604 | 605 | if (unlikely(!stmt)) { 606 | continue; 607 | } 608 | 609 | if (stmt->an == AST_AN_DECL) { 610 | const struct ast_decl *const decl = ast_data(stmt, decl); 611 | 612 | for (size_t name_idx = 0; name_idx < decl->name_count; ++name_idx) { 613 | unit->scope->objs[offset++] = (struct scope_obj) { 614 | .name = decl->names[name_idx], 615 | .obj = SCOPE_OBJ_GVAR, 616 | .decl = stmt, 617 | }; 618 | } 619 | } else if (stmt->an == AST_AN_FUNC) { 620 | const struct ast_func *const func = ast_data(stmt, func); 621 | 622 | unit->scope->objs[offset++] = (struct scope_obj) { 623 | .name = func->name, 624 | .obj = SCOPE_OBJ_FUNC, 625 | .func = stmt, 626 | }; 627 | 628 | aggr_error(&error, scope_build_inner(stmt, unit->scope)); 629 | } 630 | } 631 | 632 | aggr_error(&error, find_duplicates(unit->scope->objs, objcount)); 633 | qsort(unit->scope->objs, objcount, sizeof(struct scope_obj), cmp_scope_obj); 634 | return error; 635 | } 636 | 637 | struct scope_obj *scope_find_object(const struct scope *scope, 638 | const struct lex_symbol *const name) 639 | { 640 | const struct scope_obj needle = { .name = name }; 641 | 642 | do { 643 | struct scope_obj *const found = bsearch(&needle, scope->objs, 644 | scope->objcount, sizeof(struct scope_obj), cmp_scope_obj); 645 | 646 | if (found && found->obj != SCOPE_OBJ_DUPL) { 647 | if (found->obj == SCOPE_OBJ_AVAR) { 648 | const struct ast_decl *const decl = ast_data(found->decl, decl); 649 | return likely(name->beg > decl->names[0]->beg) ? found : NULL; 650 | } else { 651 | return found; 652 | } 653 | } 654 | } while ((scope = scope->outer)); 655 | 656 | return NULL; 657 | } 658 | 659 | ptrdiff_t scope_find_wlab(const struct ast_func *const func, 660 | const struct lex_symbol *const name) 661 | { 662 | const struct scope_obj needle = { .name = name }; 663 | const ssize_t elem_size = sizeof(*func->wlabs); 664 | 665 | const void *const found = bsearch(&needle, func->wlabs, func->wlab_count, 666 | (size_t) elem_size, cmp_scope_obj); 667 | 668 | return likely(found) ? 669 | ((char *) found - (char *) func->wlabs) / elem_size : -1; 670 | } 671 | -------------------------------------------------------------------------------- /src/scope.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct lex_symbol; 4 | struct ast_node; 5 | struct ast_func; 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | enum { 12 | SCOPE_OBJ_DUPL, // marker for duplicate symbols 13 | SCOPE_OBJ_BCON, // built-in constant 14 | SCOPE_OBJ_BFUN, // built-in function 15 | SCOPE_OBJ_GVAR, // user-defined global variable 16 | SCOPE_OBJ_AVAR, // user-defined automatic variable 17 | SCOPE_OBJ_FUNC, // user-defined function 18 | SCOPE_OBJ_PARM, // user-defined function parameter 19 | SCOPE_OBJ_COUNT, 20 | }; 21 | 22 | static_assert(SCOPE_OBJ_COUNT - 1 <= UINT8_MAX, ""); 23 | typedef uint8_t scope_obj_t; 24 | 25 | enum { 26 | SCOPE_BCON_ID_NULL, 27 | SCOPE_BCON_ID_TRUE, 28 | SCOPE_BCON_ID_FALSE, 29 | SCOPE_BCON_ID_COUNT, 30 | }; 31 | 32 | static_assert(SCOPE_BCON_ID_COUNT - 1 <= UINT8_MAX, ""); 33 | typedef uint8_t scope_bcon_id_t; 34 | 35 | struct scope_builtin_const { 36 | const struct lex_symbol *const name; 37 | const struct type *const type; 38 | }; 39 | 40 | extern const struct scope_builtin_const scope_builtin_consts[]; 41 | 42 | enum { 43 | SCOPE_BFUN_ID_NULL = 0, 44 | SCOPE_BFUN_ID_MONOTIME, 45 | SCOPE_BFUN_ID_MALLOC, 46 | SCOPE_BFUN_ID_CALLOC, 47 | SCOPE_BFUN_ID_REALLOC, 48 | SCOPE_BFUN_ID_FREE, 49 | SCOPE_BFUN_ID_PS, 50 | SCOPE_BFUN_ID_PU8, 51 | SCOPE_BFUN_ID_PI8, 52 | SCOPE_BFUN_ID_PU16, 53 | SCOPE_BFUN_ID_PI16, 54 | SCOPE_BFUN_ID_PU32, 55 | SCOPE_BFUN_ID_PI32, 56 | SCOPE_BFUN_ID_PU64, 57 | SCOPE_BFUN_ID_PI64, 58 | SCOPE_BFUN_ID_PNL, 59 | SCOPE_BFUN_ID_EXIT, 60 | SCOPE_BFUN_ID_COUNT, 61 | }; 62 | 63 | static_assert(SCOPE_BFUN_ID_COUNT - 1 <= UINT8_MAX, ""); 64 | typedef uint8_t scope_bfun_id_t; 65 | 66 | struct scope_builtin_func { 67 | const struct lex_symbol *const name; 68 | const struct type *const rettype; 69 | const size_t param_count; 70 | const struct type_nt_pair *const params; 71 | }; 72 | 73 | extern const struct scope_builtin_func scope_builtin_funcs[]; 74 | 75 | struct scope_obj { 76 | const struct lex_symbol *name; 77 | scope_obj_t obj; 78 | 79 | union { 80 | /* obj == SCOPE_OBJ_GVAR || obj == SCOPE_OBJ_AVAR */ 81 | const struct ast_node *decl; 82 | 83 | /* obj == SCOPE_OBJ_PARM */ 84 | const struct type *type; 85 | 86 | /* obj == SCOPE_OBJ_FUNC */ 87 | const struct ast_node *func; 88 | 89 | /* obj == SCOPE_OBJ_BCON */ 90 | scope_bcon_id_t bcon_id; 91 | 92 | /* obj == SCOPE_OBJ_BFUN */ 93 | scope_bfun_id_t bfun_id; 94 | }; 95 | }; 96 | 97 | struct scope { 98 | const struct scope *outer; 99 | size_t objcount; 100 | struct scope_obj objs[]; 101 | }; 102 | 103 | int scope_build(struct ast_node *); 104 | 105 | enum { 106 | SCOPE_OK = 0, 107 | SCOPE_NOMEM, 108 | SCOPE_DUPLICATED, 109 | }; 110 | 111 | struct scope_obj *scope_find_object(const struct scope *, 112 | const struct lex_symbol *); 113 | 114 | ptrdiff_t scope_find_wlab(const struct ast_func *, const struct lex_symbol *); 115 | -------------------------------------------------------------------------------- /src/type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct lex_symbol; 4 | struct ast_node; 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | enum { 13 | TYPE_VOID = 0, 14 | TYPE_U8, 15 | TYPE_I8, 16 | TYPE_U16, 17 | TYPE_I16, 18 | TYPE_U32, 19 | TYPE_I32, 20 | TYPE_U64, 21 | TYPE_I64, 22 | TYPE_USIZE, 23 | TYPE_SSIZE, 24 | TYPE_UPTR, 25 | TYPE_IPTR, 26 | TYPE_ENUM, 27 | TYPE_PTR, 28 | TYPE_VPTR, 29 | TYPE_FPTR, 30 | TYPE_QUAINT, 31 | TYPE_STRUCT, 32 | TYPE_UNION, 33 | TYPE_COUNT, 34 | }; 35 | 36 | static_assert(TYPE_COUNT - 1 <= UINT8_MAX, ""); 37 | typedef uint8_t type_t; 38 | 39 | #define type_is_integral(t) ((t) >= TYPE_U8 && (t) <= TYPE_ENUM) 40 | #define type_is_signed(t) ((t) % 2 == 0) 41 | #define type_is_unsigned(t) ((t) % 2) 42 | #define type_is_ptr(t) ((t) == TYPE_PTR || (t) == TYPE_VPTR || (t) == TYPE_FPTR) 43 | #define type_is_quaint(t) ((t) == TYPE_QUAINT) 44 | 45 | #define type_alloc calloc(1, sizeof(struct type)) 46 | 47 | struct type_nt_pair { 48 | const struct lex_symbol *name; 49 | struct type *type; 50 | }; 51 | 52 | struct type_nv_pair { 53 | const struct lex_symbol *name; 54 | uint64_t value; 55 | }; 56 | 57 | struct type { 58 | type_t t; 59 | size_t count; 60 | size_t size, alignment; 61 | 62 | union { 63 | /* t == TYPE_PTR || t == TYPE_QUAINT */ 64 | struct type *subtype; 65 | 66 | /* t == TYPE_STRUCT || t == TYPE_UNION */ 67 | struct { 68 | size_t member_count; 69 | struct type_nt_pair *members; 70 | size_t *offsets; 71 | }; 72 | 73 | /* t == TYPE_FPTR */ 74 | struct { 75 | size_t param_count; 76 | struct type_nt_pair *params; 77 | struct type *rettype; 78 | }; 79 | 80 | /* t == TYPE_ENUM */ 81 | struct { 82 | size_t value_count; 83 | struct type_nv_pair *values; 84 | type_t t_value; 85 | }; 86 | }; 87 | }; 88 | 89 | #define type(_t, _count) &(struct type) {\ 90 | .t = TYPE_##_t, \ 91 | .count = (_count), \ 92 | } 93 | 94 | #define type_ptr(_count, _subtype) &(struct type) { \ 95 | .t = TYPE_PTR, \ 96 | .count = (_count), \ 97 | .subtype = (_subtype), \ 98 | } 99 | 100 | #define type_quaint(_count, _subtype) &(struct type) { \ 101 | .t = TYPE_QUAINT, \ 102 | .count = (_count), \ 103 | .subtype = (_subtype), \ 104 | } 105 | 106 | #define type_struct(_count, _member_count, ...) &(struct type) { \ 107 | .t = TYPE_STRUCT, \ 108 | .count = (_count), \ 109 | .member_count = (_member_count), \ 110 | .members = (struct type_nt_pair[]) { \ 111 | __VA_ARGS__ \ 112 | }, \ 113 | } 114 | 115 | #define type_union(_count, _member_count, ...) &(struct type) { \ 116 | .t = TYPE_UNION, \ 117 | .count = (_count), \ 118 | .member_count = (_member_count), \ 119 | .members = (struct type_nt_pair[]) { \ 120 | __VA_ARGS__ \ 121 | }, \ 122 | } 123 | 124 | #define type_fptr(_count, _param_count, _rettype, ...) &(struct type) { \ 125 | .t = TYPE_FPTR, \ 126 | .count = (_count), \ 127 | .param_count = (_param_count), \ 128 | .params = (struct type_nt_pair[]) { \ 129 | __VA_ARGS__ \ 130 | }, \ 131 | .rettype = (_rettype), \ 132 | } 133 | 134 | #define type_enum(_count, _value_count, _t_value, ...) &(struct type) { \ 135 | .t = TYPE_ENUM, \ 136 | .count = (_count), \ 137 | .value_count = (_value_count), \ 138 | .values = (struct type_nv_pair[]) { \ 139 | __VA_ARGS__ \ 140 | }, \ 141 | .t_value = (_t_value), \ 142 | } 143 | 144 | type_t type_match(const struct lex_symbol *); 145 | const struct type *type_symtab_find(const struct lex_symbol *); 146 | int type_symtab_insert(const struct lex_symbol *, const struct type *); 147 | void type_symtab_clear(void); 148 | void type_print(FILE *, const struct type *); 149 | void type_free(const struct type *); 150 | int type_copy(struct type *, const struct type *); 151 | bool type_equals(const struct type *, const struct type *); 152 | int type_quantify(struct type *); 153 | const struct type *type_of_expr(const struct ast_node *); 154 | int type_check_ast(const struct ast_node *); 155 | 156 | enum { 157 | TYPE_OK = 0, 158 | TYPE_NOMEM, 159 | TYPE_INVALID, 160 | }; 161 | -------------------------------------------------------------------------------- /tests/ast.inappropriate-context.q: -------------------------------------------------------------------------------- 1 | /* OK, declaration in unit context */ 2 | a: byte; 3 | 4 | /* OK, type declaration in unit context */ 5 | type myint: long; 6 | 7 | /* return in unit context */ 8 | return 3 + 4 * 2; 9 | 10 | /* expression in unit context */ 11 | a = 5 + 4; 12 | 13 | /* wait label in unit context */ 14 | [my_label] 15 | 16 | /* wait statement in unit context */ 17 | wait q for 5 msec; 18 | 19 | /* control-flow statement in unit context */ 20 | while true || false { 21 | 1 + 1; 22 | } 23 | 24 | if 0 { 25 | } elif 0 { 26 | } elif 0 { 27 | } else { 28 | } 29 | 30 | /* noint block in unit context */ 31 | noint { 32 | 1 + 2; 33 | } 34 | 35 | /* lexical block in unit context */ 36 | { 37 | 3 + 4; 38 | } 39 | 40 | entry 41 | { 42 | /* nested functions are forbidden */ 43 | inner_function(a: int): int 44 | { 45 | return a + 3; 46 | } 47 | 48 | /* OK, wait label in function */ 49 | [my_label_here] 50 | 51 | /* type declaration statement in function */ 52 | type mytype: int; 53 | } 54 | -------------------------------------------------------------------------------- /tests/ast.inappropriate-wexp.q: -------------------------------------------------------------------------------- 1 | entry: int 2 | { 3 | /* sec/msec may never appear out of the top-level context of a wait-for */ 4 | 3 + 4 * 2 msec; 5 | a: byte = 5sec; 6 | 7 | /* OK */ 8 | q: quaint(u32) = null as quaint(u32); 9 | wait q for (3 + 4 * 2) sec; 10 | wait q for 150msec; 11 | result: u32 = *q; 12 | 13 | return 0:int; 14 | } 15 | -------------------------------------------------------------------------------- /tests/ast.invalid-types.q: -------------------------------------------------------------------------------- 1 | type mytype: struct( 2 | a: ptr[5](vptr), 3 | b: byte, 4 | c: quaint(u32) 5 | ); 6 | 7 | type myint: u64; 8 | 9 | entry 10 | { 11 | /* pointer without a subtype */ 12 | p: ptr(); 13 | p: ptr; 14 | 15 | /* void pointer with subtype */ 16 | p: vptr(int); 17 | 18 | /* quaint without a subtype */ 19 | q: quaint; 20 | 21 | /* OK, quaint with void subtype */ 22 | q: quaint(); 23 | 24 | /* empty struct */ 25 | s: struct(); 26 | 27 | /* non-existent type */ 28 | n: some_unknown_type; 29 | 30 | /* OK, array of size zero */ 31 | arr: u64[0]; 32 | 33 | /* OK */ 34 | s: mytype = 0 as mytype; 35 | n: myint; 36 | 37 | /* primitive type with a subtype */ 38 | arr: int[5](vptr); 39 | 40 | /* array of pointers without a subtype */ 41 | arr: ptr[5]; 42 | 43 | /* bad function pointers */ 44 | fp: fptr(/* name:type pairs expected */ 5 + 3, 4): int; 45 | fp: fptr(/* no types/names */ x, y): int; 46 | fp: fptr(/* duplicate names */ a: int, a: byte): int; 47 | fp: fptr /* must at least have () */; 48 | 49 | /* duplicate member name */ 50 | s: struct( 51 | /* p is an array(8) of pointers to pointer to byte */ 52 | p: ptr[8](ptr(byte)), 53 | 54 | /* f points to a function taking x and y, returning array(5) of int */ 55 | f: fptr(x: byte, y: int): int[5], 56 | 57 | /* union with duplicate members */ 58 | u: union(dup: byte, b: int, dup: ptr(vptr), d: uptr), 59 | 60 | /* member p already exists */ 61 | p: int 62 | ); 63 | 64 | /* duplicate name in declaration list */ 65 | same, b, same: int = 5 as int; 66 | } 67 | -------------------------------------------------------------------------------- /tests/lex.unknown-token-end.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | a: int; 4 | a = 5 as int; 5 | }/* 6 | -------------------------------------------------------------------------------- /tests/lex.unknown-token.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | a: int; 4 | a = $#5; 5 | } 6 | -------------------------------------------------------------------------------- /tests/parse.incomplete-expr.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | a: int = 5 as int + 3 * ; 4 | } 5 | -------------------------------------------------------------------------------- /tests/parse.missing-brace.q: -------------------------------------------------------------------------------- 1 | entry: int 2 | { 3 | const a, b, c: byte = 5; 4 | 5 | if c == 5 { 6 | (a + 3) != b; 7 | /* missing } */ 8 | 9 | return 0:int; 10 | } 11 | -------------------------------------------------------------------------------- /tests/parse.missing-expr.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | do { 4 | 1 + 2; 5 | } while; 6 | } 7 | -------------------------------------------------------------------------------- /tests/parse.missing-op.q: -------------------------------------------------------------------------------- 1 | entry: int 2 | { 3 | const a, b, c: byte = 5; 4 | 5 | if c == 5 { 6 | a + b c; 7 | } 8 | 9 | return 0:int; 10 | } 11 | -------------------------------------------------------------------------------- /tests/parse.missing-paren.q: -------------------------------------------------------------------------------- 1 | entry: int 2 | { 3 | const a, b, c: byte = 5; 4 | 5 | if c == 5 { 6 | (a + c) * 3) != b; 7 | } 8 | 9 | return 0:int; 10 | } 11 | -------------------------------------------------------------------------------- /tests/parse.missing-semi.q: -------------------------------------------------------------------------------- 1 | entry: int 2 | { 3 | const a, b, c: byte = 5; 4 | 5 | if c == 5 { 6 | (a + 3) != b /* missing ; here */ 7 | } 8 | 9 | return 0:int; 10 | } 11 | -------------------------------------------------------------------------------- /tests/q.q: -------------------------------------------------------------------------------- 1 | use ""; 2 | use "libs/trig/math" as math; 3 | use "libs/abc/math" as default; 4 | 5 | type a: struct(a: int, b: byte, c: long, d: u8); 6 | exposed type b: union(c: int); 7 | 8 | exposed type e1: enum(red, green, blue): u32; 9 | exposed type e2: enum(abc, def, dd): u32; 10 | exposed type e3: e2[3]; 11 | // math::e1::green, e1::green 12 | 13 | exposed xxx: ptr(int[15]) = null as ptr(int[15]); 14 | 15 | exposed entry 16 | { 17 | pu32(fibonacci(32:u32)), pnl(); 18 | fib1: quaint(u32) = ~fibonacci(32 as u32); 19 | fib2: quaint(u32) = ~fibonacci(30 as u32); 20 | iter: u32 = 0:u32; 21 | aa, bb: e1; 22 | aa = bb; 23 | 24 | [%glab] 25 | const pipe_id: int = 3; [@wlab] 26 | 27 | send item, sizeof(t); 28 | 29 | //send data, sizeof(data) to 30 | 31 | //spawn 5; 32 | 33 | if id == 1 { 34 | q <- 4; 35 | } elif id == 2 { 36 | q -> i; 37 | } else { 38 | 39 | } 40 | 41 | for idx_i(0, 4): int { 42 | for idx_j(0, 2): long { 43 | test(idx_i, idx_j); 44 | } 45 | } 46 | 47 | match(expr, case_expr, val_expr, case_expr, val_expr); 48 | match(expr, 3, foo, 5, bar); 49 | 50 | match ball.color { 51 | case red, green, blue, yellow { 52 | test(); 53 | test(); 54 | } 55 | 56 | case black, silver, white: 57 | 58 | } 59 | } 60 | 61 | spawn 3; 62 | 63 | x: e1 = e1::blue + e2::abc; 64 | ps("x: "), pu32(x:u32), pnl(); 65 | //ps("eq: "), pu8(x == e2::aa), pnl(); 66 | ps("Test: "), pu8(123), pnl(); 67 | 68 | do { 69 | wait fib1 for 1000 msec; 70 | //wait fib2 for 500 msec; 71 | pu32(iter++:u32), pnl(); 72 | ps("Another string "), ps("yet another"), pnl(); 73 | } while !fib1@end; 74 | 75 | pu32(*fib1), pnl(), pu32(*fib2), pnl(); 76 | } 77 | 78 | test 79 | { 80 | q: queue(struct(a: int, b: int)) = mq(8); 81 | 82 | for idx(start: 0, stop: 4, step: 1): int { 83 | 84 | } 85 | 86 | q -> 3; 87 | q <- 3; 88 | } 89 | 90 | factorial(number: byte): ulong 91 | { 92 | if number == 1 { 93 | return 1:ulong; 94 | } 95 | 96 | return factorial(number - 1) * number:ulong; 97 | } 98 | 99 | /* very slow, exponential fibonacci */ 100 | fibonacci(number: u32): u32 101 | { 102 | if number == 0:u32 || number == 1:u32 { 103 | return number; 104 | } else { 105 | return fibonacci(number - 1:u32) + fibonacci(number - 2:u32); 106 | } 107 | } 108 | 109 | /* mutual recursion */ 110 | gcd_ternary(m: u64, n: u64): u64 111 | { 112 | return m == n ? m : (m > n ? gcd_elif(m - n, n) : gcd_ternary(m, n - m)); 113 | } 114 | 115 | gcd_elif(m: u64, n: u64): u64 116 | { 117 | if m == n { 118 | return m; 119 | } elif m > n { 120 | return gcd_ternary(m - n, n); 121 | } else { 122 | return gcd_elif(m, n - m); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /tests/scope.duplicates.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | my_autovar: int; 4 | 5 | { 6 | another_autovar: byte = 0; 7 | } 8 | 9 | { 10 | /* OK, the previous declaration is in a different lexical block */ 11 | another_autovar: int; 12 | 13 | { 14 | /* this shadows the outer variable */ 15 | another_autovar: int; 16 | } 17 | } 18 | 19 | my_autovar: byte; 20 | } 21 | 22 | my_gvar: int[5]; 23 | const another_gvar: uptr; 24 | 25 | do_some_work: byte 26 | { 27 | return 0; 28 | } 29 | 30 | /* different signature but same name */ 31 | do_some_work(a: byte): int 32 | { 33 | return a:int + 0:int; 34 | } 35 | 36 | /* functions and variables live in the same namespace, so this is also a dupe */ 37 | do_some_work: int; 38 | 39 | /* attempt to redefine a builtin */ 40 | exit: vptr; 41 | 42 | /* my_gvar is already defined at unit scope */ 43 | my_gvar: long; 44 | -------------------------------------------------------------------------------- /tests/type.args-mismatch.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | f1(); 4 | f1(5, 6, 7); 5 | f1(5:int, 6, 7:u64, 1337); 6 | 7 | f2(3); 8 | f2(256, 256); 9 | 10 | f3(1); 11 | f3(1, 2); 12 | 13 | f4(1, 2); 14 | f4(1); 15 | 16 | /* void type is not convertible */ 17 | f4() as int; 18 | 19 | /* type mismatch: byte + void */ 20 | 3 + f4(); 21 | 22 | /* fptr does not match args */ 23 | fp: fptr(a: int): u16 = f2; 24 | 25 | /* fptr does not match return type */ 26 | fp: fptr(a: byte, b: byte) = f2; 27 | fp: fptr(a: byte, b: byte): u64 = f2; 28 | 29 | /* OK */ 30 | const fp1: fptr(a: int, b: byte, c: u64): uptr = f1; 31 | f1(5:int, 6, 7:u64), fp1(5:int, 6, 7:u64); 32 | 33 | const fp2: fptr(a: byte, b: byte): u16 = f2; 34 | f2(255, 255), fp2(255, 255); 35 | 36 | const fp3: fptr(): byte = f3; 37 | f3(), fp3(); 38 | 39 | const fp4: fptr() = f4; 40 | f4(), fp4(); 41 | } 42 | 43 | f1(a: int, b: byte, c: u64): uptr 44 | { 45 | return (a:u64 + b:u64 + c:u64) as uptr; 46 | } 47 | 48 | f2(a: byte, b: byte): u16 49 | { 50 | return a:u16 + b:u16; 51 | } 52 | 53 | f3: byte 54 | { 55 | return 255; 56 | } 57 | 58 | f4 59 | { 60 | return; 61 | } 62 | -------------------------------------------------------------------------------- /tests/type.binop-mismatch.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | /* 255 is of type byte (u8), 256 is of type ushort (u16) */ 4 | 255 == 256; 5 | 6 | /* OK */ 7 | 255:u16 == 256; 8 | 255 as u16 == 256; 9 | 10 | /* 5 is of type byte (u8), not int (i32) */ 11 | a: int = 5; 12 | 13 | /* OK */ 14 | b: int = 5:int; 15 | c: int = 5 as int; 16 | 17 | /* the ternary operator requires the same type and signedness */ 18 | true ? 255 : 256; 19 | 20 | /* byte (u8) unsigned : sbyte (i8) signed */ 21 | true ? 5 : -5; 22 | 23 | /* OK */ 24 | true ? (255:u16) : 256; 25 | true ? (5:sbyte) : -5; 26 | 27 | /* && returns byte (u8) */ 28 | d: int = true && false; 29 | 30 | /* OK */ 31 | e: int = (true && false) as int; 32 | 33 | p: ptr(u64) = null as ptr(u64); 34 | 35 | /* pointer arithmetic requires a second operand of type usize */ 36 | p + 3; 37 | 38 | /* OK */ 39 | p + 3:usize; 40 | 41 | /* all bitwise operations require same-sized unsigned integral types */ 42 | 64 << 2:sbyte; 43 | 64 & 32:int; 44 | 64 ^ 32:int; 45 | ^5:int; 46 | 47 | /* OK */ 48 | 64 << 2; 49 | 64 & 32; 50 | 64 ^ 32; 51 | ^5; 52 | } 53 | -------------------------------------------------------------------------------- /tests/type.forbidden-pointer-arith.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | vp: vptr = null; 4 | 5 | /* arithmetic on void pointer */ 6 | vp++, ++vp, vp--, --vp; 7 | vp += 2:usize; 8 | vp -= 2:usize; 9 | vp + 3:usize; 10 | 11 | /* arithmetic on function pointer */ 12 | fp: fptr(a: int): int = null as fptr(a: int): int; 13 | fp++, ++fp, fp--, --fp; 14 | fp += 2:usize; 15 | fp -= 2:usize; 16 | fp + 3:usize; 17 | 18 | /* dereferencing a vptr or a fptr */ 19 | *vp; 20 | *fp; 21 | 22 | /* OK */ 23 | *(vp as ptr(int)); 24 | } 25 | -------------------------------------------------------------------------------- /tests/type.lvalue-required.q: -------------------------------------------------------------------------------- 1 | type mytype: struct(a: int, b: byte, c: byte[3]); 2 | 3 | entry 4 | { 5 | /* non-lvalue subexpressions */ 6 | 5 = 3; 7 | 5 += 3; 8 | 5 >>= 3; 9 | ((5 + 4) && false) = 3; 10 | entry() = null:fptr(); 11 | !0 = 1; 12 | 6++, ++7, 8--, --9; 13 | null++ && true-- && &false; 14 | &10; 15 | (false ? 1 : 2) = 3; 16 | 17 | /* attempting to modify a const-qualified variable */ 18 | const c: byte[3] = 0 as byte[3]; 19 | c[1]++, ++c[1], c[1]--, --c[1]; 20 | c[2] = 5; 21 | 22 | /* attempting to modify a builtin fptr */ 23 | exit = null as fptr(s: u32): u32; 24 | 25 | /* OK, a dereferenced pointer is an lvalue */ 26 | *(null as ptr(int) + 42:usize) = 5:int; 27 | 28 | /* the value of a quaint is not an lvalue, though */ 29 | *(null as quaint(int)) = 5:int; 30 | 31 | /* 32 | The cast is not an lvalue and the * requires an lvalue 33 | because it needs to free and nullify the quaint itself. 34 | */ 35 | a: int = *(null as quaint(int)); 36 | 37 | s: mytype = 0 as mytype; 38 | ps: ptr(mytype) = &s; 39 | 40 | /* OK */ 41 | s.a = 3:int; 42 | s.b = 3; 43 | s.c[1] = 3; 44 | 45 | /* OK */ 46 | ps->a = 3:int; 47 | ps->b = 3; 48 | ps->c[1] = 3; 49 | 50 | const p: ptr(ptr(ptr(byte))) = null as ptr(ptr(ptr(byte))); 51 | 52 | /* OK */ 53 | ***p = 3; 54 | **p = null:ptr(byte); 55 | *p = null:ptr(ptr(byte)); 56 | 57 | /* the p pointer itself is constant */ 58 | p = null as ptr(ptr(ptr(byte))); 59 | } 60 | -------------------------------------------------------------------------------- /tests/type.return-mismatch.q: -------------------------------------------------------------------------------- 1 | a: int 2 | { 3 | /* returning void but function requires int */ 4 | return; 5 | 6 | /* returning byte but function requires int */ 7 | return 5; 8 | 9 | /* OK */ 10 | return 5:int; 11 | } 12 | 13 | b 14 | { 15 | /* returning byte but function requires void */ 16 | return 6; 17 | 18 | /* OK */ 19 | return; 20 | } 21 | 22 | c: ptr(struct(x: u64, y: u64)) 23 | { 24 | correct: ptr(struct(x: u64, y: u64)); 25 | incorrect: ptr(struct(x: u32, y: u64)); 26 | 27 | /* type mismatch */ 28 | return incorrect; 29 | 30 | /* OK */ 31 | return correct; 32 | } 33 | -------------------------------------------------------------------------------- /tests/type.wait-label-mismatch.q: -------------------------------------------------------------------------------- 1 | entry 2 | { 3 | q: quaint() = ~f1(); 4 | 5 | wait q until f1::non_existent_label; 6 | wait q until non_existent_func::label_a; 7 | wait q until f1; 8 | wait q until non_existent_func; 9 | q@somewhere; 10 | q@f1::non_existent_label; 11 | q@non_existent_func::label_e; 12 | 13 | /* OK */ 14 | q@start; 15 | q@f1::label_a; 16 | q@f2::label_d; 17 | q@f3::label_g; 18 | q@end; 19 | wait q until f1::label_a; 20 | wait q until f1::label_b; 21 | wait q until f1::label_c; 22 | wait q until f2::label_d; 23 | wait q until f2::label_e; 24 | wait q until f2::label_f; 25 | wait q until f3::label_g; 26 | wait q until f3::label_h; 27 | wait q until f3::label_i; 28 | *q; 29 | } 30 | 31 | f1 32 | { 33 | [label_a] 34 | [label_b] 35 | [label_c] 36 | } 37 | 38 | f2 39 | { 40 | [label_d] 41 | [label_e] 42 | [label_f] 43 | } 44 | 45 | f3 46 | { 47 | [label_g] 48 | [label_h] 49 | [label_i] 50 | } 51 | --------------------------------------------------------------------------------