├── .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 |
--------------------------------------------------------------------------------