├── .gitignore ├── tests ├── testharness.req ├── Makefile.in ├── testharness.c ├── lexical │ ├── names.bli │ ├── literals.bli │ ├── lexfuncs2.bli │ ├── lexfuncs1.bli │ └── macros.bli ├── expression │ ├── structures.bli │ ├── operators.bli │ └── controlexp.bli └── runtests.py ├── cmake ├── config.h.in └── Modules │ └── FindLLVM.cmake ├── include └── blissc │ ├── charfuncs.h │ ├── libgen.h │ ├── declarations.h │ ├── gencode.h │ ├── macros.h │ ├── lexer.h │ ├── structures.h │ ├── switches.h │ ├── support │ ├── logging.h │ ├── fileio.h │ ├── strings.h │ ├── utils.h │ └── statcodes.h │ ├── listings.h │ ├── execfuncs.h │ ├── driver.h │ ├── scanner.h │ ├── machinedef.h │ ├── parser.h │ ├── nametable.h │ ├── symbols.h │ └── lexeme.h ├── lib ├── support │ ├── utils.c │ ├── statcodes.c │ ├── logging.c │ └── strings.c ├── llvmgen │ ├── llvm_helper.h │ ├── llvm_machinectx.h │ ├── llvm_helper.cpp │ ├── llvm_machines.c │ └── llvmgen.h ├── frontend │ ├── libgen.c │ ├── switches.c │ ├── execfuncs.c │ └── lexeme.c └── driver │ └── driver.c ├── LICENSE.TXT ├── configure.ac ├── README.md ├── CMakeLists.txt ├── Makefile.am └── driver └── blissc.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | build/ 3 | cmake-build*/ 4 | *.xcodeproj/ 5 | .DS_Store 6 | .idea/ 7 | -------------------------------------------------------------------------------- /tests/testharness.req: -------------------------------------------------------------------------------- 1 | KEYWORDMACRO 2 | test_setup (numcases) = 3 | EXTERNAL ROUTINE test_printf; 4 | GLOBAL ROUTINE test_init = 5 | BEGIN 6 | RETURN numcases; 7 | END%; 8 | MACRO 9 | test_output (num, fmt)[] = 10 | test_printf(UPLIT(%STRING('TEST %03d: ', fmt, %CHAR(10), %CHAR(0))), 11 | num %IF NOT %NULL(%REMAINING) %THEN , %REMAINING %FI)%; 12 | -------------------------------------------------------------------------------- /cmake/config.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * config.h.in for CMake builds 3 | * Copyright (C) 2018-2020, Matthew Madison. 4 | * Distributed under license. See LICENSE.TXT for details. 5 | */ 6 | #define BLISSC_HOST_TRIPLE "@LLVM_HOST_TRIPLE@" 7 | #define BLISSC_VERSION_MAJOR @blissc_VERSION_MAJOR@ 8 | #define BLISSC_VERSION_MINOR @blissc_VERSION_MINOR@ 9 | #define PACKAGE_NAME "@CMAKE_PROJECT_NAME@" 10 | #define PACKAGE_VERSION "@blissc_VERSION@" 11 | -------------------------------------------------------------------------------- /include/blissc/charfuncs.h: -------------------------------------------------------------------------------- 1 | #ifndef charfuncs_h__ 2 | #define charfuncs_h__ 3 | /* 4 | *++ 5 | * charfuncs.h - definitions for CH$ functions. 6 | * 7 | * Copyright © 2013, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "expression.h" 13 | #include "nametable.h" 14 | 15 | void charfuncs_init(expr_ctx_t ctx, scopectx_t scope); 16 | 17 | #endif /* charfuncs_h__ */ 18 | -------------------------------------------------------------------------------- /lib/support/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * utils.c - Miscellaneous utility functions 4 | * 5 | * This module contains miscellaneous utility functions 6 | * used by other modules. 7 | * 8 | * Copyright © 2012-2020, Matthew Madison. 9 | * All rights reserved. 10 | * Distributed under license. See LICENSE.TXT for details. 11 | *-- 12 | */ 13 | 14 | /* 15 | * bits_needed 16 | * 17 | * Calculates the number of bits needed to hold 18 | * a binary integer value. 19 | */ 20 | long 21 | bits_needed (unsigned long val) 22 | { 23 | long count = 0; 24 | while (val != 0) { 25 | count += 1; 26 | val = val >> 1; 27 | } 28 | return count; 29 | } /* bits_needed */ 30 | -------------------------------------------------------------------------------- /lib/llvmgen/llvm_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef llvm_helper_h__ 2 | #define llvm_helper_h__ 3 | /* 4 | *++ 5 | * llvm_helper.h - Additional LLVM C APIs 6 | * 7 | * Copyright © 2013, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #ifdef __cplusplus 13 | 14 | extern "C" { 15 | #endif 16 | 17 | void HelperSetAllocaAlignment(LLVMValueRef Inst, unsigned int Bytes); 18 | char *HelperGetDefaultTriple(void); 19 | LLVMTargetRef HelperLookupTarget(const char *triple, char **err); 20 | void HelperSetAsmVerbosity(LLVMTargetMachineRef tm, LLVMBool v); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | #endif /* llvm_helper_h__ */ 26 | -------------------------------------------------------------------------------- /tests/Makefile.in: -------------------------------------------------------------------------------- 1 | # tests/Makefile.in 2 | # 3 | # Provides a 'make check' target compatible with 4 | # the top-level Automake-generated makefile. 5 | # 6 | # Copyright (c) 2013, Matthew Madison. 7 | # All rights reserved. 8 | # Distributed under license. See LICENSE.TXT for details. 9 | 10 | BLISSC = @top_builddir@/blissc 11 | TESTDIR = @top_srcdir@/tests 12 | PYTHON = @PYTHON@ 13 | 14 | .PHONY: check all install distdir dist 15 | 16 | check: testharness.lib 17 | $(PYTHON) "$(TESTDIR)/runtests.py" --blissc="$(BLISSC)" --cc="$(CC)" "$(TESTDIR)" 18 | 19 | testharness.lib: $(TESTDIR)/testharness.req 20 | $(BLISSC) --library -o $@ $< 21 | 22 | clean: 23 | @files=`find $(TESTDIR) -name '*.bli' -print`; \ 24 | for srcf in $$files ; do f=`basename $$srcf .bli`; \ 25 | echo "rm -f $$f $$f.o" ; rm -f $$f $$f.o ; done 26 | 27 | all install distdir dist: 28 | @ : 29 | -------------------------------------------------------------------------------- /include/blissc/libgen.h: -------------------------------------------------------------------------------- 1 | #ifndef libgen_h__ 2 | #define libgen_h__ 3 | /* 4 | *++ 5 | * libgen.h - LIBRARY generation definitions. 6 | * 7 | * Copyright © 2013-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include "machinedef.h" 14 | #include "support/logging.h" 15 | #include "support/fileio.h" 16 | #include "support/utils.h" 17 | 18 | struct libgen_ctx_s; 19 | typedef struct libgen_ctx_s *libgen_ctx_t; 20 | 21 | int lib_parse_header(logctx_t logctx, textpos_t curpos, 22 | filectx_t fh, compilerinfo_t *me, machinedef_t *mach); 23 | libgen_ctx_t libgen_init(fioctx_t fioctx, const char *libname, size_t lnlen, 24 | compilerinfo_t *compilerinfo); 25 | int libgen_parse(libgen_ctx_t lgctx, void *exprctx); 26 | void libgen_finish(libgen_ctx_t lgctx); 27 | 28 | #endif /* libgen_h__ */ 29 | -------------------------------------------------------------------------------- /tests/testharness.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | * testharness.c 3 | * 4 | * Simple test harness for running BLISS tests. 5 | * 6 | * Copyright © 2013, Matthew Madison. 7 | * All rights reserved. 8 | * Distributed under license. See LICENSE.TXT for details. 9 | *-- 10 | */ 11 | #include 12 | #include 13 | #include 14 | 15 | int TEST_INIT(void); 16 | void RUNTEST(int); 17 | 18 | void 19 | TEST_PRINTF (const char *fmt, ...) 20 | { 21 | va_list ap; 22 | 23 | va_start(ap, fmt); 24 | 25 | vprintf(fmt, ap); 26 | 27 | } 28 | 29 | int 30 | main (int argc, char *argv[]) 31 | { 32 | int i, numtests; 33 | 34 | numtests = TEST_INIT(); 35 | if (argc < 2) { 36 | for (i = 1; i <= numtests; i++) 37 | RUNTEST(i); 38 | return 0; 39 | } 40 | 41 | for (i = 1; i < argc; i++) { 42 | int tno = atoi(argv[i]); 43 | if (tno >= 1 && tno <= numtests) { 44 | RUNTEST(tno); 45 | } 46 | } 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/lexical/names.bli: -------------------------------------------------------------------------------- 1 | MODULE test = 2 | BEGIN 3 | 4 | LIBRARY 'testharness.lib'; 5 | 6 | LITERAL 7 | ABCDEFGHIJKLMNOPQRSTUVWXYZ12345 = 31, 8 | $_$_$_$_$_$_$_$_$_$_$_$_$_$_$_$ = 666, 9 | $abcdefghijklmnopqrs_0123456789 = 999, 10 | %NAME ('This name can have any arbitrary sequence of characters') = 12345; 11 | !! cerr %BLISS-W-NAMETOOLON, 12 | 13 | test_setup(numcases=4); 14 | 15 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 16 | BEGIN 17 | CASE .caseno FROM 1 TO 4 OF 18 | SET 19 | [1] : test_output(.caseno, '%d', ABCDEFGHIJKLMNOPQRSTUVWXYZ12345); 20 | !! 1 31 21 | [2] : test_output(.caseno, '%d', $_$_$_$_$_$_$_$_$_$_$_$_$_$_$_$); 22 | !! 2 666 23 | [3] : test_output(.caseno, '%d', $abcdefghijklmnopqrs_0123456789); 24 | !! 3 999 25 | [4] : test_output(.caseno, '%d', 26 | %NAME ('This name can have any arbitrary sequence of characters')); 27 | ! will not trigger warning, just truncation 28 | !! 4 12345 29 | TES; 30 | END; 31 | 32 | END 33 | ELUDOM 34 | -------------------------------------------------------------------------------- /include/blissc/declarations.h: -------------------------------------------------------------------------------- 1 | #ifndef declarations_h__ 2 | #define declarations_h__ 3 | /* 4 | *++ 5 | * declarations.h - Definitions for declarations. 6 | * 7 | * Copyright © 2012-2013, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "structures.h" 13 | #include "expression.h" 14 | #include "nametable.h" 15 | #include "parser.h" 16 | #include "lexeme.h" 17 | #include "machinedef.h" 18 | 19 | struct declctx_s; 20 | typedef struct declctx_s *declctx_t; 21 | 22 | declctx_t declarations_init(expr_ctx_t ctx, parse_ctx_t pctx, 23 | scopectx_t kwdscope, machinedef_t *mach); 24 | void declarations_finish(declctx_t dctx); 25 | name_t *define_plit(expr_ctx_t ctx, lextype_t curlt, textpos_t pos); 26 | int parse_decl_name(parse_ctx_t pctx, strdesc_t **result, textpos_t *pos); 27 | int parse_declaration(expr_ctx_t ectx); 28 | int declare_module(expr_ctx_t ectx); 29 | int parse_libgen_declarations(expr_ctx_t ectx); 30 | 31 | #endif /* declarations_h__ */ 32 | -------------------------------------------------------------------------------- /include/blissc/gencode.h: -------------------------------------------------------------------------------- 1 | #ifndef gencode_h__ 2 | #define gencode_h__ 3 | /* 4 | *++ 5 | * gencode.h - Generic interface for gencode modules. 6 | * 7 | * Copyright © 2012, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include "nametable.h" 14 | #include "symbols.h" 15 | #include "support/logging.h" 16 | #include "machinedef.h" 17 | 18 | struct gencodectx_s; 19 | typedef struct gencodectx_s *gencodectx_t; 20 | 21 | gencodectx_t gencode_init(void *ectx, logctx_t logctx, machinedef_t *mach, symctx_t symctx); 22 | void gencode_finish(gencodectx_t gctx); 23 | void gencode_postinit(gencodectx_t gctx); 24 | void gencode_finish(gencodectx_t gctx); 25 | int gencode_module_begin(gencodectx_t gctx, name_t *np); 26 | int gencode_module_end(gencodectx_t gctx, name_t *np); 27 | int gencode_routine_begin(gencodectx_t gctx, name_t *np); 28 | int gencode_routine_end(gencodectx_t gctx, name_t *np); 29 | void gencode_optlevel_set(gencodectx_t gctx, unsigned int level); 30 | 31 | #endif /* gencode_h__ */ 32 | -------------------------------------------------------------------------------- /lib/llvmgen/llvm_machinectx.h: -------------------------------------------------------------------------------- 1 | #ifndef llvm_machinectx_h__ 2 | #define llvm_machinectx_h__ 3 | /* 4 | *++ 5 | * llvm_machinectx.h - LLVM "machine" context. 6 | * 7 | * Copyright © 2013, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include "blissc/machinedef.h" 14 | #ifndef __STDC_LIMIT_MACROS 15 | #define __STDC_LIMIT_MACROS 16 | #endif 17 | #ifndef __STDC_CONSTANT_MACROS 18 | #define __STDC_CONSTANT_MACROS 19 | #endif 20 | #include "llvm-c/Core.h" 21 | #include "llvm-c/Analysis.h" 22 | #include "llvm-c/Target.h" 23 | #include "llvm-c/TargetMachine.h" 24 | #include "llvm_helper.h" 25 | 26 | struct machine_ctx_s { 27 | void *genctx; 28 | LLVMContextRef llvmctx; 29 | LLVMTargetMachineRef target_machine; 30 | LLVMCodeGenFileType outputtype; 31 | char *outfile; 32 | char *irdumpfile; 33 | char *triple; 34 | int is_macho; 35 | }; 36 | 37 | #endif /* llvm_machinectx_h__ */ 38 | -------------------------------------------------------------------------------- /include/blissc/macros.h: -------------------------------------------------------------------------------- 1 | #ifndef macros_h__ 2 | #define macros_h__ 3 | /* 4 | *++ 5 | * macros.h - Definitions for macros. 6 | * 7 | * Copyright © 2012, 2013, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "expression.h" 13 | #include "nametable.h" 14 | #include "parser.h" 15 | #include "lexeme.h" 16 | 17 | struct macroctx_s; 18 | typedef struct macroctx_s *macroctx_t; 19 | 20 | macroctx_t macros_init(scopectx_t scope, expr_ctx_t ctx); 21 | void macros_finish(macroctx_t mctx); 22 | int macro_paramlist(expr_ctx_t ctx, scopectx_t curscope, int assign_allowed, 23 | int for_macro, lextype_t closers[], int nclosers, 24 | scopectx_t *ptable, namereflist_t *plist); 25 | int declare_macro(expr_ctx_t ctx, scopectx_t scope, lextype_t curlt); 26 | lexseq_t *macparam_lexseq(name_t *np); 27 | name_t *macparam_special(scopectx_t scope, strdesc_t *pname, lexseq_t *seqval); 28 | name_t *macparam_lookup(lexctx_t lctx, scopectx_t scope, 29 | strdesc_t *pname, lexseq_t *value); 30 | 31 | #endif /* macros_h__ */ 32 | -------------------------------------------------------------------------------- /include/blissc/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef lexer_h__ 2 | #define lexer_h__ 3 | /* 4 | *++ 5 | * lexer.h - Lexer interface definitions. 6 | * 7 | * Copyright © 2012-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "lexeme.h" 13 | #include "nametable.h" 14 | #include "scanner.h" 15 | #include "support/logging.h" 16 | #include "support/strings.h" 17 | 18 | struct lexer_ctx_s; 19 | typedef struct lexer_ctx_s *lexer_ctx_t; 20 | 21 | lexer_ctx_t lexer_init(strctx_t strctx, scopectx_t kwdscope, 22 | logctx_t logctx, void *fioctx); 23 | lexctx_t lexer_lexctx(lexer_ctx_t lctx); 24 | void *lexer_scanctx(lexer_ctx_t ctx); 25 | int lexer_fopen(lexer_ctx_t ctx, const char *fname, size_t fnlen, char **actnamep); 26 | int lexer_popen(lexer_ctx_t ctx, scan_input_fn infn, void *fnctx); 27 | void lexer_finish(lexer_ctx_t ctx); 28 | lexeme_t *lexer_next(lexer_ctx_t ctx, int erroneof, textpos_t *posp); 29 | lexeme_t *lexer_peek(lexer_ctx_t ctx, int erroneof); 30 | void lexer_insert(lexer_ctx_t ctx, lexeme_t *lex); 31 | void lexer_insert_seq(lexer_ctx_t ctx, lexseq_t *seq); 32 | 33 | #endif /* lexer_h__ */ 34 | -------------------------------------------------------------------------------- /include/blissc/structures.h: -------------------------------------------------------------------------------- 1 | #ifndef structures_h__ 2 | #define structures_h__ 3 | /* 4 | *++ 5 | * structures.h - Definitions for structures 6 | * 7 | * Copyright © 2012, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "expression.h" 13 | #include "nametable.h" 14 | #include "lexeme.h" 15 | 16 | struct strudef_s; 17 | typedef struct strudef_s strudef_t; 18 | struct sym_data_s; 19 | 20 | void structures_init(expr_ctx_t ctx, scopectx_t kwdscope); 21 | int declare_structure(expr_ctx_t ctx, scopectx_t scope); 22 | int declare_field(expr_ctx_t ctx, scopectx_t scope); 23 | int structure_allocate(expr_ctx_t ctx, name_t *struname, 24 | strudef_t **strup, unsigned int *units, 25 | scopectx_t *scopep, int is_ref); 26 | expr_node_t *structure_reference(expr_ctx_t pctx, name_t *struname, 27 | int ctce_accessors, name_t *segname, 28 | lexeme_t *lex); 29 | lexeme_t *field_extract(name_t *fnp, unsigned int which); 30 | lexseq_t *field_lexseq(name_t *fnp); 31 | namereflist_t *fieldset_reflist(name_t *fsnp); 32 | 33 | #endif /* structures_h__ */ 34 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2024, Matthew Madison. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 | THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /include/blissc/switches.h: -------------------------------------------------------------------------------- 1 | #ifndef switches_h__ 2 | #define switches_h__ 3 | /* 4 | *++ 5 | * switches.h - Compiler switches definitions. 6 | * 7 | * Copyright © 2012, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include "parser.h" 14 | #include "nametable.h" 15 | #include "lexeme.h" 16 | 17 | typedef int (*switch_parse_handler_fn)(parse_ctx_t pctx, void *ctx, 18 | lextype_t dcltype, lexeme_t *swlex); 19 | typedef int (*toggle_handler_fn)(parse_ctx_t pctx, void *ctx, int togidx, 20 | lextype_t dcltype, name_t *togname); 21 | int switch_special_declare(scopectx_t scope, lextype_t swtype, 22 | switch_parse_handler_fn handler, void *ctx); 23 | int switch_toggle_declare(scopectx_t scope, strdesc_t *name, 24 | toggle_handler_fn handler, void *ctx, int togidx, 25 | name_t **on_name, name_t **off_name); 26 | int value_toggle_declare(scopectx_t scope, strdesc_t *name, 27 | toggle_handler_fn handler, void *ctx, int togidx, 28 | name_t **on_name, name_t **off_name); 29 | int value_toggle_dispatch(parse_ctx_t pctx, void *ctx, lextype_t dcltype, 30 | name_t *togname); 31 | int parse_switches(parse_ctx_t pctx, lextype_t decltype, lextype_t terminator); 32 | 33 | #endif /* switches_h__ */ 34 | -------------------------------------------------------------------------------- /include/blissc/support/logging.h: -------------------------------------------------------------------------------- 1 | #ifndef logging_h__ 2 | #define logging_h__ 3 | /* 4 | *++ 5 | * logging.h - Logging definitions. 6 | * 7 | * Copyright © 2013, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include 13 | #include 14 | #include "strings.h" 15 | #include "statcodes.h" 16 | #include "utils.h" 17 | 18 | struct logctx_s; 19 | typedef struct logctx_s *logctx_t; 20 | typedef strdesc_t * (*filename_fetch_fn)(void *, int); 21 | typedef strdesc_t * (*line_fetch_fn)(void *, int, unsigned int); 22 | typedef void (*lstg_print_fn)(void *, const char *buf, size_t buflen, int aln); 23 | 24 | #define LOG_MAXERRS_DEFAULT 10 25 | 26 | logctx_t logging_init(jmp_buf returnpoint); 27 | unsigned int log_maxerrs(logctx_t ctx); 28 | void log_maxerrs_set(logctx_t ctx, unsigned int maxerrs); 29 | void log_fetchfn_set(logctx_t ctx, filename_fetch_fn fn, void *ffctx); 30 | void log_linefetchfn_set(logctx_t ctx, line_fetch_fn fn, void *lfctx); 31 | void log_lstgprintfn_set(logctx_t ctx, lstg_print_fn fn, void *lpctx); 32 | void log_logterm_set(logctx_t ctx, int val); 33 | void log_logsrctolst_set(logctx_t ctx, int val); 34 | unsigned int log_infocount(logctx_t ctx); 35 | unsigned int log_warncount(logctx_t ctx); 36 | unsigned int log_errcount(logctx_t ctx); 37 | void log_signal(logctx_t ctx, textpos_t pos, statcode_t code, ...); 38 | void log_vsignal(logctx_t ctx, textpos_t pos, statcode_t code, va_list ap); 39 | void log_message(logctx_t ctx, const char *buf, size_t buflen); 40 | void logging_finish(logctx_t ctx); 41 | 42 | #endif /* logging_h__ */ 43 | -------------------------------------------------------------------------------- /lib/llvmgen/llvm_helper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * llvm_helper.cpp - Implements C APIs for parts of LLVM. 4 | * 5 | * 6 | * LLVM provides C-callable APIs for most, but not all, 7 | * of the functionality we need. This module contains 8 | * helper functions to provide C APIs for the handful 9 | * of other functions not already provided. 10 | * 11 | * Copyright © 2013-2024, Matthew Madison. 12 | * All rights reserved. 13 | * Distributed under license. See LICENSE.TXT for details. 14 | *-- 15 | */ 16 | #include "llvm-c/TargetMachine.h" 17 | #include "llvm/Target/TargetMachine.h" 18 | #include "llvm_helper.h" 19 | #include "llvm/IR/Instructions.h" 20 | 21 | #include "llvm/MC/TargetRegistry.h" 22 | #include "llvm/Support/Alignment.h" 23 | #if LLVM_VERSION_MAJOR >= 17 24 | #include "llvm/TargetParser/Host.h" 25 | #else 26 | #include "llvm/Support/Host.h" 27 | #endif 28 | 29 | using namespace llvm; 30 | 31 | void HelperSetAllocaAlignment(LLVMValueRef Inst, unsigned int Bytes) { 32 | reinterpret_cast(Inst)->setAlignment(Align(Bytes)); 33 | } 34 | 35 | char *HelperGetDefaultTriple(void) { 36 | return strdup(sys::getDefaultTargetTriple().c_str()); 37 | } 38 | 39 | LLVMTargetRef HelperLookupTarget(const char *triple, char **err) { 40 | std::string error; 41 | const Target* target = TargetRegistry::lookupTarget(triple, error); 42 | if (!error.empty()) *err = strdup(error.c_str()); 43 | return reinterpret_cast(const_cast(target)); 44 | } 45 | 46 | void HelperSetAsmVerbosity(LLVMTargetMachineRef tm, LLVMBool v) { 47 | reinterpret_cast(tm)->Options.MCOptions.AsmVerbose = (bool) v; 48 | } 49 | -------------------------------------------------------------------------------- /include/blissc/listings.h: -------------------------------------------------------------------------------- 1 | #ifndef listings_h__ 2 | #define listings_h__ 3 | /* 4 | *++ 5 | * listings.h - Listings definitions. 6 | * 7 | * Copyright © 2012, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "nametable.h" 13 | #include "support/logging.h" 14 | #include 15 | 16 | struct lstgctx_s; 17 | typedef struct lstgctx_s *lstgctx_t; 18 | 19 | typedef enum { 20 | LISTOPT_SRC, 21 | LISTOPT_REQ, 22 | LISTOPT_EXP, 23 | LISTOPT_TRC, 24 | LISTOPT_LIB, 25 | LISTOPT_OBJ, 26 | LISTOPT_ASM, 27 | LISTOPT_SYM, 28 | LISTOPT_BIN, 29 | LISTOPT_COM 30 | } listopt_type_t; 31 | #define LISTOPT_COUNT (LISTOPT_COM+1) 32 | 33 | lstgctx_t listings_init(scopectx_t kwdscope, logctx_t logctx); 34 | void listings_finish(lstgctx_t ctx); 35 | void listing_mainscope_set(lstgctx_t ctx, scopectx_t scope); 36 | void listing_name_set(lstgctx_t ctx, strdesc_t *str); 37 | void listing_title_set(lstgctx_t ctx, strdesc_t *str); 38 | void listing_subtitle_set(lstgctx_t ctx, strdesc_t *str); 39 | void listing_compilerid_set(lstgctx_t ctx, strdesc_t *str); 40 | void listing_ident_set(lstgctx_t ctx, strdesc_t *str); 41 | void listing_printline(void *, const char *, size_t, int); 42 | int listing_printsrc(void *, char *, size_t, unsigned int, char); 43 | int listing_open(lstgctx_t ctx, const char *fname, size_t len, 44 | const char *lname, size_t llen, unsigned int flags); 45 | void listing_file_close(void *); 46 | void listing_require_begin(lstgctx_t ctx, char *fname, size_t fnlen); 47 | void listing_newblock(lstgctx_t ctx); 48 | void listing_endblock(lstgctx_t ctx, scopectx_t this_scope); 49 | 50 | #endif /* listings_h__ */ 51 | -------------------------------------------------------------------------------- /tests/lexical/literals.bli: -------------------------------------------------------------------------------- 1 | MODULE test = 2 | BEGIN 3 | 4 | LIBRARY 'testharness.lib'; 5 | 6 | LITERAL 7 | a = 65535, 8 | b = -1, 9 | c = %DECIMAL'-10', 10 | d = %O'177', ! 127 decimal 11 | e = %B'1101', ! 13 decimal 12 | f = %X'7F', 13 | g = %C''''; 14 | 15 | BIND 16 | h = UPLIT('untyped string literal', %CHAR(0)), 17 | i = UPLIT(%ASCII'%ASCII string literal', %CHAR(0)), 18 | j = UPLIT(%ASCIC'counted string literal') : VECTOR [,BYTE], 19 | k = UPLIT(%ASCIZ'null-terminated string literal'); 20 | 21 | test_setup(numcases=11); 22 | 23 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 24 | BEGIN 25 | CASE .caseno FROM 1 TO 11 OF 26 | SET 27 | [1] : test_output(.caseno, 'a = [%d,0x%x]', a, a); 28 | !! 1 a = [65535,0xffff] 29 | [2] : test_output(.caseno, 'b = [%d,0x%x]', b, b); 30 | !! 2 b = [-1,0xffffffff] 31 | [3] : test_output(.caseno, 'c = [%d,0x%x]', c, c); 32 | !! 3 c = [-10,0xfffffff6] 33 | [4] : test_output(.caseno, 'd = [%d,0x%x]', d, d); 34 | !! 4 d = [127,0x7f] 35 | [5] : test_output(.caseno, 'e = [%d,0x%x]', e, e); 36 | !! 5 e = [13,0xd] 37 | [6] : test_output(.caseno, 'f = [%d,0x%x]', f, f); 38 | !! 6 f = [127,0x7f] 39 | [7] : test_output(.caseno, 'g = ["%c",0x%x]', g, g); 40 | !! 7 g = ["'",0x27] 41 | [8] : test_output(.caseno, 'h = "%s"', h); 42 | !! 8 h = "untyped string literal" 43 | [9] : test_output(.caseno, 'i = "%s"', i); 44 | !! 9 i = "%ASCII string literal" 45 | [10] : test_output(.caseno, 'j = "%-*.*s" (length=%d)', 46 | .j[0], .j[0], j[1], .j[0]); 47 | !!10 j = "counted string literal" (length=22) 48 | [11] : test_output(.caseno, 'k = "%s"', k); 49 | !!11 k = "null-terminated string literal" 50 | TES; 51 | END; 52 | 53 | END 54 | ELUDOM 55 | -------------------------------------------------------------------------------- /include/blissc/execfuncs.h: -------------------------------------------------------------------------------- 1 | #ifndef execfuncs_h__ 2 | #define execfuncs_h__ 3 | /* 4 | *++ 5 | * execfuncs.h - Definitions for executable functions. 6 | * 7 | * Copyright © 2012-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "expression.h" 13 | #include "nametable.h" 14 | #include "support/strings.h" 15 | 16 | typedef int (*compare_fn)(long, long); 17 | typedef expr_node_t *(*execfunchandler_fn)(expr_ctx_t ctx, 18 | compare_fn fn, name_t *fnp, 19 | exprseq_t *arglist, textpos_t curpos); 20 | typedef void *(*execfuncgenerator_fn)(void *ctx, void *fctx, 21 | expr_node_t *node, void *extra); 22 | 23 | #define FUNC_M_VARARGS (1U<<0U) 24 | #define FUNC_M_NOVALUE (1U<<1U) 25 | #define FUNC_M_NOPARSE (1U<<2U) // do not parse arguments 26 | #define FUNC_M_BUILTIN (1U<<3U) 27 | 28 | struct funcdef_s { 29 | execfunchandler_fn handler; 30 | execfuncgenerator_fn generator; 31 | compare_fn fn; 32 | void *genfnctx; 33 | unsigned int numargs; // min # of args when VARARGS 34 | unsigned int flags; 35 | size_t namelen; 36 | char name[NAME_SIZE]; 37 | }; 38 | 39 | typedef struct funcdef_s funcdef_t; 40 | 41 | /* 42 | * FUNCDEF(name, handler, contextptr, argcount, flags) 43 | */ 44 | #define FUNCDEF(n_, h_, c_, a_, f_) { .handler = (h_), .fn = (c_), \ 45 | .numargs = (a_), .flags = (f_), \ 46 | .namelen = sizeof(n_)-1, .name = n_ } 47 | 48 | void execfunc_init(expr_ctx_t ctx, scopectx_t scope); 49 | name_t *execfunc_define(scopectx_t ctx, funcdef_t *funcdef, textpos_t pos); 50 | 51 | #endif /* execfuncs_h__ */ 52 | -------------------------------------------------------------------------------- /include/blissc/support/fileio.h: -------------------------------------------------------------------------------- 1 | #ifndef fileio_h__ 2 | #define fileio_h__ 3 | /* 4 | *++ 5 | * fileio.h - File I/O definitions. 6 | * 7 | * Copyright © 2013-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include 13 | #include 14 | #include "logging.h" 15 | 16 | struct filectx_s; 17 | typedef struct filectx_s *filectx_t; 18 | struct fioctx_s; 19 | typedef struct fioctx_s *fioctx_t; 20 | 21 | fioctx_t fileio_init(logctx_t logctx); 22 | void fileio_finish(fioctx_t fio); 23 | 24 | struct fio_pathparts_s { 25 | char *path_fullname; 26 | size_t path_fullnamelen; 27 | char *path_dirname; 28 | size_t path_dirnamelen; 29 | char *path_filename; 30 | size_t path_filenamelen; 31 | char *path_suffix; 32 | size_t path_suffixlen; 33 | int path_absolute; 34 | }; 35 | typedef struct fio_pathparts_s fio_pathparts_t; 36 | 37 | char *file_canonicalname(fioctx_t fio, const char *orig, ssize_t origlen, size_t *lenp); 38 | int file_splitname(fioctx_t fio, const char *orig, ssize_t origlen, int canoncialize, 39 | fio_pathparts_t *parts); 40 | int file_combinename(fioctx_t fio, fio_pathparts_t *parts); 41 | void file_freeparts(fioctx_t fio, fio_pathparts_t *parts); 42 | 43 | filectx_t file_open_input(fioctx_t fio, const char *fname, size_t fnlen); 44 | filectx_t file_open_output(fioctx_t fio, const char *fname, size_t fnlen); 45 | void file_close(filectx_t ctx); 46 | char *file_getname(filectx_t ctx); 47 | int file_readline(filectx_t ctx, char *buf, size_t bufsiz, size_t *len); 48 | int file_readbuf(filectx_t ctx, void *buf, size_t bufsiz, size_t *len); 49 | int file_writeline(filectx_t ctx, const char *buf, size_t buflen); 50 | int file_writebuf(filectx_t ctx, const void *buf, size_t buflen); 51 | 52 | #endif /* fileio_h__ */ 53 | -------------------------------------------------------------------------------- /tests/expression/structures.bli: -------------------------------------------------------------------------------- 1 | MODULE test = 2 | BEGIN 3 | 4 | LIBRARY 'testharness.lib'; 5 | 6 | FIELD 7 | fld0 = [0,0,4,0], 8 | flds = SET 9 | fld1 = [0,4,4,0], 10 | fld2 = [1,0,4,0], 11 | fld3 = [1,4,4,0] 12 | TES; 13 | OWN 14 | blk : BLOCK [2,BYTE] FIELD (fld0,flds), 15 | vec : VECTOR [4] INITIAL (1,2,3,4), 16 | yesno : VECTOR [2] INITIAL(UPLIT(%ASCIZ'NO'),UPLIT(%ASCIZ'YES')); 17 | 18 | BIND 19 | gbword = blk : WORD, 20 | gbwarr = blk : VECTOR [,WORD]; 21 | 22 | test_setup(numcases=10); 23 | 24 | ROUTINE testref (caseno, v : REF VECTOR) = 25 | BEGIN 26 | IF .caseno EQL 1 THEN 27 | RETURN .v 28 | ELSE IF .caseno LEQ 5 THEN 29 | RETURN .v[.caseno-2] 30 | ELSE 31 | RETURN 0 32 | END; 33 | 34 | ROUTINE testbvec (idx, bvarg) = 35 | BEGIN 36 | LOCAL bv : BITVECTOR[%BPVAL] INITIAL(.bvarg); 37 | RETURN .bv[.idx]; 38 | END; 39 | 40 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 41 | BEGIN 42 | 43 | IF .caseno EQL 1 THEN 44 | test_output(.caseno, '%s', 45 | .yesno[vec EQLA testref(.caseno, vec)]) 46 | !! 1 YES 47 | ELSE IF .caseno LEQ 5 THEN 48 | test_output(.caseno, '%ld', testref(.caseno, vec)) 49 | !! 2 1 50 | !! 3 2 51 | !! 4 3 52 | !! 5 4 53 | ELSE IF .caseno LEQ 9 THEN 54 | test_output(.caseno, '%d', testbvec(.caseno-5, %B'1010')) 55 | !! 6 1 56 | !! 7 0 57 | !! 8 1 58 | !! 9 0 59 | ELSE 60 | BEGIN 61 | BIND 62 | bword = blk : WORD, 63 | bwarr = blk : VECTOR [,WORD]; 64 | blk [fld0] = .vec[0]; 65 | blk [fld1] = .vec[1]; 66 | blk [fld2] = .vec[2]; 67 | blk [fld3] = .vec[3]; 68 | test_output(.caseno, '%x,%x,%x,%x', 69 | .bword, .gbword, .bwarr[0], .gbwarr[0]); 70 | !! 10 4321,4321,4321,4321 71 | END; 72 | 73 | END; 74 | 75 | END 76 | ELUDOM 77 | -------------------------------------------------------------------------------- /tests/expression/operators.bli: -------------------------------------------------------------------------------- 1 | MODULE test = 2 | BEGIN 3 | 4 | LIBRARY 'testharness.lib'; 5 | 6 | 7 | OWN 8 | yesno : VECTOR [2] INITIAL(UPLIT(%ASCIZ'NO'),UPLIT(%ASCIZ'YES')); 9 | 10 | test_setup(numcases=29); 11 | 12 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 13 | BEGIN 14 | LOCAL 15 | a, b, val; 16 | 17 | a = 12; 18 | b = 6; 19 | 20 | CASE .caseno FROM 1 TO 29 OF 21 | SET 22 | [1] : val = +.a; 23 | !! 1 12 24 | [2] : val = -.a; 25 | !! 2 -12 26 | [3] : val = .a + .b; 27 | !! 3 18 28 | [4] : val = -.a + (-.b); 29 | !! 4 -18 30 | [5] : val = .a - .b; 31 | !! 5 6 32 | [6] : val = .a * .b; 33 | !! 6 72 34 | [7] : val = .a / .b; 35 | !! 7 2 36 | [8] : val = .a MOD .b; 37 | !! 8 0 38 | [9] : val = .a ^ .b; 39 | !! 9 768 40 | [10] : val = .a EQL .b; 41 | !! 10 0 42 | [11] : val = .a GTR .b; 43 | !! 11 1 44 | [12] : val = .a LSS .b; 45 | !! 12 0 46 | [13] : val = .a GEQ .b; 47 | !! 13 1 48 | [14] : val = .a LEQ .b; 49 | !! 14 0 50 | [15] : val = NOT .a; 51 | !! 15 -13 52 | [16] : val = .a AND .b; 53 | !! 16 4 54 | [17] : val = .a OR .b; 55 | !! 17 14 56 | [18] : val = .a XOR .b; 57 | !! 18 10 58 | [19] : val = .a EQV .b; 59 | !! 19 -11 60 | [20] : val = (a = .b); 61 | !! 20 6 62 | [21] : val = .a + .b * 2; 63 | !! 21 24 64 | [22] : val = .a * 2 + .b; 65 | !! 22 30 66 | [23] : val = a = .b EQLU 6; 67 | !! 23 1 68 | [24] : val = .a * (2 + .b); 69 | !! 24 96 70 | [25] : val = .a / .b + .a MOD .b; 71 | !! 25 2 72 | [26] : val = .a / 3 * .b + 5; 73 | !! 26 29 74 | [27] : val = .a * 1; 75 | !! 27 12 76 | [28] : val = .b * 0; 77 | !! 28 0 78 | [29] : val = .a + .b * 2 + .a / 3; 79 | !! 29 28 80 | TES; 81 | test_output(.caseno, '%ld', .val); 82 | 83 | END; 84 | 85 | END 86 | ELUDOM 87 | -------------------------------------------------------------------------------- /include/blissc/driver.h: -------------------------------------------------------------------------------- 1 | #ifndef blissc_driver_h__ 2 | #define blissc_driver_h__ 3 | /* 4 | *++ 5 | * driver.h - compiler "driver" interface 6 | * 7 | * Copyright © 2013, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include 14 | 15 | struct blissc_driverctx_s; 16 | typedef struct blissc_driverctx_s *blissc_driverctx_t; 17 | 18 | typedef enum { 19 | BLISS_K_OUTPUT_ASSEMBLY, 20 | BLISS_K_OUTPUT_OBJECT, 21 | BLISS_K_OUTPUT_LIBRARY 22 | } bliss_output_t; 23 | 24 | // XXX - bit positions must match the enum in 25 | // listings.h 26 | #define BLISS_M_LIST_SRC (1<<0) 27 | #define BLISS_M_LIST_REQ (1<<1) 28 | #define BLISS_M_LIST_EXP (1<<2) 29 | #define BLISS_M_LIST_TRC (1<<3) 30 | #define BLISS_M_LIST_LIB (1<<4) 31 | #define BLISS_M_LIST_OBJ (1<<5) 32 | #define BLISS_M_LIST_ASM (1<<6) 33 | #define BLISS_M_LIST_BIN (1<<7) 34 | #define BLISS_M_LIST_COM (1<<8) 35 | 36 | blissc_driverctx_t blissc_init(jmp_buf retenv); 37 | int blissc_target_set(blissc_driverctx_t ctx, const char *machspec); 38 | int blissc_output_set(blissc_driverctx_t ctx, bliss_output_t outtype, 39 | const char *fname, int fnlen); 40 | int blissc_listopt_set(blissc_driverctx_t ctx, unsigned int listflags, 41 | const char *fname, int fnlen); 42 | int blissc_variant_set(blissc_driverctx_t ctx, unsigned int val); 43 | int blissc_optlevel_set(blissc_driverctx_t ctx, unsigned int val); 44 | int blissc_dumpir_set(blissc_driverctx_t ctx, int val, const char *fname, 45 | int fnlen); 46 | int blissc_compile(blissc_driverctx_t ctx, const char *fname, int fnlen); 47 | void blissc_finish(blissc_driverctx_t ctx); 48 | const char *blissc_package_name(void); 49 | const char *blissc_package_version(void); 50 | int blissc_searchpath_add(blissc_driverctx_t ctx, const char *path, int pathlen); 51 | 52 | #endif /* blissc_driver_h__ */ 53 | -------------------------------------------------------------------------------- /include/blissc/scanner.h: -------------------------------------------------------------------------------- 1 | #ifndef scanner_h__ 2 | #define scanner_h__ 3 | /* 4 | *++ 5 | * scanner.h - Scanner definitions. 6 | * 7 | * Copyright © 2012, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "blissc/support/logging.h" 13 | #include "blissc/support/strings.h" 14 | 15 | struct scanctx_s; 16 | typedef struct scanctx_s *scanctx_t; 17 | struct streamctx_s; 18 | typedef struct streamctx_s *streamctx_t; 19 | 20 | typedef enum { 21 | SCANTYPE_IDENTIFIER, 22 | SCANTYPE_DECLITERAL, 23 | SCANTYPE_QUOTEDSTRING, 24 | SCANTYPE_OPERATOR, 25 | SCANTYPE_PUNCTUATION, 26 | SCANTYPE_END, 27 | SCANTYPE_ERR_EOF, 28 | SCANTYPE_ERR_FIO, 29 | SCANTYPE_ERR_INVLIT, 30 | SCANTYPE_ERR_INVID, 31 | SCANTYPE_ERR_QSTR, 32 | SCANTYPE_ERR_INVCHR 33 | } scantype_t; 34 | 35 | #define SCAN_M_ERRONEOF (1<<0) 36 | #define SCAN_M_SIGNOK (1<<1) 37 | 38 | static inline int __attribute__((unused)) scan_ok (scantype_t typ) { 39 | return (typ < SCANTYPE_ERR_EOF); 40 | } 41 | 42 | typedef int (*scan_input_fn)(void *ctx, char *buf, size_t siz, size_t *len); 43 | typedef int (*scan_list_fn)(void *ctx, char *buf, size_t len, 44 | unsigned int lineno, char lexcode); 45 | typedef void (*scan_close_fn)(void *ctx); 46 | 47 | scanctx_t scan_init(strctx_t strctx, logctx_t logctx, void *fioctx); 48 | void scan_listfuncs_set(scanctx_t sctx, scan_list_fn lfunc, 49 | scan_close_fn cfunc, void *ctx); 50 | streamctx_t scan_fopen(scanctx_t ctx, const char *fname, size_t fnlen, char **actnamep); 51 | streamctx_t scan_popen(scanctx_t ctx, scan_input_fn infn, void *fnctx); 52 | scantype_t scan_getnext(streamctx_t ctx, unsigned int flags, 53 | strdesc_t **tok, unsigned int *lineno, 54 | unsigned int *column); 55 | strdesc_t *scan_curline_get(streamctx_t ctx); 56 | void scan_close(streamctx_t ctx); 57 | void scan_finish(scanctx_t ctx); 58 | 59 | #endif /* scanner_h__ */ 60 | -------------------------------------------------------------------------------- /tests/lexical/lexfuncs2.bli: -------------------------------------------------------------------------------- 1 | MODULE test = 2 | BEGIN 3 | 4 | LIBRARY 'testharness.lib'; 5 | 6 | LITERAL 7 | z = 3 + 4, 8 | y = z + 2 * 5, 9 | bpvaltest1 = -7 : SIGNED(%BPVAL), 10 | bpvaltest2 = 255 : UNSIGNED(%BPVAL); 11 | 12 | FIELD 13 | testfield = [0,4,4,0]; 14 | 15 | GLOBAL 16 | testglob; 17 | 18 | OWN 19 | testown, 20 | yesno : VECTOR [2] INITIAL(UPLIT(%ASCIZ'NO'),UPLIT(%ASCIZ'YES')); 21 | 22 | test_setup(numcases=10); 23 | 24 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 25 | BEGIN 26 | LOCAL 27 | testlocal, 28 | testalloc : VECTOR [2]; 29 | 30 | testglob = 1; 31 | testown = 2; 32 | testlocal = 3; 33 | 34 | CASE .caseno FROM 1 TO 10 OF 35 | SET 36 | [1] : test_output(.caseno, '%s,%s', .yesno[%CTCE(z)],.yesno[%CTCE(y)]); 37 | !! 1 YES,YES 38 | [2] : test_output(.caseno, '%d,%d,%d,%d', %FIELDEXPAND(testfield)); 39 | !! 2 0,4,4,0 40 | [3] : test_output(.caseno, '%d,%d', bpvaltest1, bpvaltest2); 41 | !! 3 -7,255 42 | [4] : test_output(.caseno, '%s,%s', 43 | .yesno[%CTCE(testglob)], .yesno[%LTCE(testglob)]); 44 | !! 4 NO,YES 45 | [5] : test_output(.caseno, '%s,%s', 46 | .yesno[%CTCE(testown)], .yesno[%LTCE(testown)]); 47 | !! 5 NO,YES 48 | [6] : test_output(.caseno, '%s,%s', 49 | .yesno[%CTCE(testlocal)], .yesno[%LTCE(testlocal)]); 50 | !! 6 NO,NO 51 | [7] : test_output(.caseno, '%s,%s', 52 | .yesno[%CTCE(.testglob)], .yesno[%LTCE(.testglob)]); 53 | !! 7 NO,NO 54 | [8] : test_output(.caseno, '%s', 55 | .yesno[%ALLOCATION(testalloc) EQLU %UPVAL * 2]); 56 | !! 8 YES 57 | [9] : test_output(.caseno, 58 | %STRING(%NUMBER(y),'+',%NUMBER(z),'=',%NUMBER(y+z))); 59 | !! 9 17+7=24 60 | [10] : test_output(.caseno, '%s,%s', .yesno[%DECLARED(testglob)], 61 | .yesno[%DECLARED(nosuchname)]); 62 | !! 10 YES,NO 63 | TES; 64 | 65 | END; 66 | 67 | END 68 | ELUDOM 69 | -------------------------------------------------------------------------------- /include/blissc/support/strings.h: -------------------------------------------------------------------------------- 1 | #ifndef strings_h__ 2 | #define strings_h__ 3 | /* 4 | *++ 5 | * strings.h - string-handling definitions. 6 | * 7 | * Copyright © 2013-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include 13 | 14 | struct strctx_s; 15 | typedef struct strctx_s *strctx_t; 16 | 17 | /* 18 | * String descriptor structure. 19 | */ 20 | typedef struct { 21 | unsigned int flags; 22 | #define STR_M_STATIC (1U<<0U) 23 | size_t len; 24 | char *ptr; 25 | } strdesc_t; 26 | 27 | /* 28 | * Macros for static string descriptor setup 29 | * at declaration time. 30 | */ 31 | #define STRDEF(s) { STR_M_STATIC, sizeof(s)-1, (char *)s } 32 | #define STRZDEF(s) { STR_M_STATIC, sizeof(s), (char *)s } 33 | 34 | /* 35 | * Inline routine for initializing a static string 36 | * descriptor. 37 | */ 38 | static inline __attribute__((unused)) strdesc_t * 39 | strdesc_init (strdesc_t *dsc, char *s, size_t len) 40 | { 41 | dsc->flags = STR_M_STATIC; 42 | dsc->len = len; 43 | dsc->ptr = s; 44 | return dsc; 45 | } 46 | 47 | /* 48 | * STRING_MAXLEN is maximum length for a literal string that 49 | * the compiler will accept. 50 | */ 51 | #define STRING_MAXLEN 1024 52 | 53 | strctx_t strings_init(void); 54 | void strings_finish(strctx_t strctx); 55 | strdesc_t *string_from_chrs(strctx_t strctx, strdesc_t *dest, 56 | const char *cp, size_t len); 57 | strdesc_t *ascic_string_from_chrs(strctx_t strctx, strdesc_t *dest, 58 | const char *cp, size_t len); 59 | strdesc_t *string_append(strctx_t strctx, strdesc_t *trg, strdesc_t *add); 60 | strdesc_t *string_alloc(strctx_t strctx, strdesc_t *dest, size_t len); 61 | strdesc_t *string_copy(strctx_t strctx, strdesc_t *dest, strdesc_t *src); 62 | void string_free(strctx_t strctx, strdesc_t *dsc); 63 | int strings_eql(strdesc_t *a, strdesc_t *b); 64 | strdesc_t *string_printf(strctx_t strctx, strdesc_t *dst, const char *fmt, ...); 65 | int string_numval(strdesc_t *str, int base, long *valp); 66 | 67 | #endif /* strings_h__ */ 68 | -------------------------------------------------------------------------------- /tests/lexical/lexfuncs1.bli: -------------------------------------------------------------------------------- 1 | %TITLE'Test of %TITLE' 2 | %SBTTL'Test of %SBTTL' 3 | MODULE test = 4 | BEGIN 5 | 6 | LIBRARY 'testharness.lib'; 7 | 8 | OWN 9 | teststrings : VECTOR [4] INITIAL ( 10 | UPLIT(%STRING (%CHAR(65), %CHAR(66), %CHAR(67), %CHAR(0))), 11 | UPLIT(%EXACTSTRING (32, %C' ', 'Padded to 32'), %CHAR(0)), 12 | UPLIT(%STRING('CHARCOUNT = ', %NUMBER(%CHARCOUNT('abc')), %CHAR(0))), 13 | UPLIT(%STRING(%EXPLODE('An explosion'), %CHAR(0)))), 14 | yesno : VECTOR [2] INITIAL(UPLIT(%ASCIZ'NO'),UPLIT(%ASCIZ'YES')); 15 | 16 | test_setup(numcases=16); 17 | 18 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 19 | BEGIN 20 | CASE .caseno FROM 1 TO 16 OF 21 | SET 22 | [1 TO 4] : test_output(.caseno, '"%s"', .teststrings[.caseno-1]); 23 | !! 1 "ABC" 24 | !! 2 "Padded to 32 " 25 | !! 3 "CHARCOUNT = 3" 26 | !! 4 "An explosion" 27 | [5] : test_output(.caseno, '%%NBITS(7,5,2)=%d', %NBITS(7,5,2)); 28 | !! 5 %NBITS(7,5,2)=3 29 | [6] : test_output(.caseno, '%%NBITS(-7)=%d', %NBITS(-7)); 30 | !! 6 %NBITS(-7)=4 31 | [7] : test_output(.caseno, '%%NBITS(-8)=%d', %NBITS(-8)); 32 | !! 7 %NBITS(-8)=4 33 | [8] : test_output(.caseno, '%%NBITSU(7)=%d', %NBITSU(7)); 34 | !! 8 %NBITSU(7)=3 35 | [9] : test_output(.caseno, '%s', .yesno[%NBITSU(-8) EQLU %BPVAL]); 36 | !! 9 YES 37 | [10] : test_output(.caseno, '%s', .yesno[%IDENTICAL(A+B,a+b)]); 38 | !! 10 YES 39 | [11] : test_output(.caseno, '%s', 40 | .yesno[%IDENTICAL(A+B,%REMOVE((A+B)))]); 41 | !! 11 YES 42 | [12] : test_output(.caseno, '%s', 43 | .yesno[%IDENTICAL(A+B,%REMOVE([A+B]))]); 44 | !! 12 YES 45 | [13] : test_output(.caseno, '%s', 46 | .yesno[%IDENTICAL(A+B,%REMOVE())]); 47 | !! 13 YES 48 | [14] : test_output(.caseno, '%s', .yesno[%BLISS(BLISSM)]); 49 | !! 14 YES 50 | [15] : test_output(.caseno, '%d', %VARIANT); 51 | !! 15 0 52 | [16] : test_output(.caseno, '%s', .yesno[%DECLARED(teststrings)]); 53 | !! 16 YES 54 | TES; 55 | 56 | %MESSAGE('Test of %MESSAGE') 57 | !! cerr % Test of %MESSAGE 58 | %INFORM('Test of %INFORM') 59 | !! cerr %BLISS-I-INFORM, Test of %INFORM 60 | %WARN('Test of %WARN') 61 | !! cerr %BLISS-W-USRWARN, Test of %WARN 62 | %ERROR('Test of %ERROR') 63 | !! cerr %BLISS-E-USRERR, Test of %ERROR 64 | %PRINT('Test of %PRINT') 65 | END; 66 | 67 | END 68 | ELUDOM 69 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl ++ 2 | dnl configure.ac - autoconf script for blissc 3 | dnl 4 | dnl Copyright (c) 2013-2024, Matthew Madison. 5 | dnl All rights reserved. 6 | dnl 7 | dnl Distributed under license. See LICENSE.TXT for details. 8 | dnl -- 9 | 10 | AC_INIT([blissc], [0.5.0], [http://github.com/madisongh/blissc/issues]) 11 | AC_DEFINE([BLISSC_VERSION_MAJOR], [0], [Major version of blissc]) 12 | AC_DEFINE([BLISSC_VERSION_MINOR], [5], [Minor version of blissc]) 13 | AC_DEFINE([BLISSC_VERSION_MAINT], [0], [Maintenance level of blissc]) 14 | AM_INIT_AUTOMAKE([subdir-objects foreign]) 15 | AM_SILENT_RULES([yes]) 16 | AC_COPYRIGHT([Copyright (c) 2013-2024, Matthew Madison]) 17 | 18 | AC_PREREQ(2.69) 19 | 20 | AC_CANONICAL_HOST 21 | AC_DEFINE_UNQUOTED([BLISSC_HOST_TRIPLE], ["$host"], [Compiler host triple]) 22 | 23 | dnl Locate the LLVM installation through use of the llvm-config script. 24 | dnl If the path is not provided by the user, assume it is in the path. 25 | 26 | AC_ARG_WITH([llvm-config], 27 | AS_HELP_STRING([--with-llvm-config@<:@=PATH@:>@], [specify llvm-config script location]), 28 | AS_IF([test "$withval" = "yes"], [ac_llvm_config=`which llvm-config`], 29 | [ac_llvm_config="$withval"]), 30 | [ac_llvm_config=`which llvm-config`]) 31 | AS_IF([test -e "$ac_llvm_config"],[ 32 | export ac_llvm_includedir=`$ac_llvm_config --includedir` 33 | export ac_llvm_libdir=`$ac_llvm_config --libdir` 34 | export ac_llvm_cflags=`$ac_llvm_config --cflags` 35 | export ac_llvm_cxxflags=`$ac_llvm_config --cxxflags` 36 | export ac_llvm_ldflags=`$ac_llvm_config --ldflags` 37 | export ac_llvm_libs=`$ac_llvm_config --libs` 38 | export ac_llvm_syslibs=`$ac_llvm_config --system-libs 2>/dev/null | grep -v '^ *$'`], 39 | AC_MSG_ERROR([[could not find working llvm-config]])) 40 | AC_SUBST(LLVM_INCLUDEDIR,[$ac_llvm_includedir]) 41 | AC_SUBST(LLVM_LIBDIR,[$ac_llvm_libdir]) 42 | AC_SUBST(LLVM_LIBS,[$ac_llvm_libs]) 43 | AC_SUBST(LLVM_SYSLIBS,[$ac_llvm_syslibs]) 44 | AC_SUBST(LLVM_CFLAGS,[$ac_llvm_cflags]) 45 | AC_SUBST(LLVM_CXXFLAGS,[$ac_llvm_cxxflags]) 46 | AC_SUBST(LLVM_LDFLAGS,[$ac_llvm_ldflags]) 47 | 48 | AC_PROG_CXX 49 | AC_PROG_CC 50 | AC_PROG_RANLIB 51 | AM_PATH_PYTHON([3.0],,[:]) 52 | AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != :]) 53 | 54 | AC_CONFIG_FILES([Makefile tests/Makefile]) 55 | AC_CONFIG_HEADERS([include/blissc/config.h:config.h.in]) 56 | AC_OUTPUT 57 | -------------------------------------------------------------------------------- /tests/expression/controlexp.bli: -------------------------------------------------------------------------------- 1 | MODULE test = 2 | BEGIN 3 | 4 | LIBRARY 'testharness.lib'; 5 | 6 | 7 | test_setup(numcases=10); 8 | 9 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 10 | BEGIN 11 | LOCAL 12 | val; 13 | 14 | IF .caseno GEQ 1 AND .caseno LEQ 3 THEN 15 | BEGIN 16 | val = (IF .caseno EQL 1 THEN 7654 17 | ELSE IF .caseno EQL 2 THEN 3210 18 | ELSE 9876); 19 | !! 1 7654 20 | !! 2 3210 21 | !! 3 9876 22 | END 23 | ELSE IF .caseno EQL 4 THEN 24 | BEGIN 25 | LOCAL n; 26 | n = 1; 27 | val = 0; 28 | WHILE .n LEQ 100 DO 29 | BEGIN 30 | val = .val + .n; 31 | n = .n * 10; 32 | END; 33 | !! 4 111 34 | END 35 | ELSE IF .caseno EQL 5 THEN 36 | BEGIN 37 | LOCAL n; 38 | n = 1000; 39 | val = 111; 40 | DO 41 | BEGIN 42 | n = .n / 10; 43 | val = .val - .n; 44 | END 45 | UNTIL .n LEQ 1; 46 | !! 5 0 47 | END 48 | ELSE IF .caseno EQL 6 THEN 49 | BEGIN 50 | val = 1; 51 | INCR i FROM 0 TO 100 BY 10 DO val = .val + .i; 52 | DECR i FROM 100 TO 0 BY 10 DO val = .val - .i; 53 | !! 6 1 54 | END 55 | ELSE IF .caseno EQL 7 THEN 56 | BEGIN 57 | LOCAL n; 58 | n = 0; 59 | val = (WHILE .n LSS 100 DO 60 | BEGIN 61 | n = .n + 20; 62 | IF .n GEQ 40 THEN EXITLOOP .n * 5; 63 | END); 64 | !! 7 200 65 | END 66 | ELSE IF .caseno EQL 8 THEN 67 | BEGIN 68 | SELECTONE .caseno OF 69 | SET 70 | [8] : val = 999; 71 | [OTHERWISE] : val = 0; 72 | TES; 73 | !! 8 999 74 | END 75 | ELSE IF .caseno EQL 9 THEN 76 | BEGIN 77 | SELECT .caseno OF 78 | SET 79 | [8] : val = 999; 80 | [OTHERWISE] : val = 0; 81 | [ALWAYS] : val = 333; 82 | TES; 83 | !! 9 333 84 | END 85 | ELSE IF .caseno EQL 10 THEN 86 | BEGIN 87 | LABEL outer; 88 | val = 0; 89 | outer: BEGIN 90 | INCR i FROM 1 TO 100 DO 91 | BEGIN 92 | val = .val + .i; 93 | IF .i GEQ 20 THEN LEAVE outer; 94 | END; 95 | END; ! outer 96 | !! 10 210 97 | END; 98 | test_output(.caseno, '%ld', .val); 99 | 100 | END; 101 | 102 | END 103 | ELUDOM 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README for the BLISS-M Compiler 2 | 3 | This is the source package for the BLISS-M compiler, a portable 4 | cross-compiler for the BLISS programming language. 5 | 6 | ## What is BLISS? 7 | The BLISS language was developed at Carnegie-Mellon University in the 1970's for systems programming. 8 | It was adopted and used extensively by Digital Equipment Corporation for development of systems 9 | software across their 16-bit, 32-bit, 36-bit, and 64-bit systems. It is a typeless, block-structured, 10 | language with an extensive lexical processing (macro) facility. See the 11 | [Wikipedia article on BLISS](http://en.wikipedia.org/wiki/BLISS) for more information about the language. 12 | 13 | 14 | ## Current Status 15 | 16 | Work in progress. The front-end is mostly complete, with a fully 17 | functioning parser and macro facility. Back-end support is currently 18 | limited to LLVM and x86 CPUs (32- or 64-bit), with a limited amount 19 | of machine-specific support. 20 | 21 | ## Prerequisites 22 | 23 | * Recent-vintage C compiler 24 | * Recent version of LLVM (one that has support for opaque pointers) 25 | * CMake 3.13 or later, or a recent version of autotools 26 | 27 | Recent development and testing has been on Ubuntu 24.04 with 28 | gcc 13 and LLVM 14, 17, and 18. 29 | 30 | ## Building the compiler 31 | 32 | First, clone [the repository](https://github.com/madisongh/blissc.git). 33 | 34 | ### Using Autotools 35 | 1. cd to the top-level source directory and run `autoreconf -i`. 36 | 2. If you want to build outside the source tree, cd to your 37 | build directory. 38 | 3. Run the `configure` script that was generated in step 2. If 39 | `llvm-config` is not in your PATH, use the `--with-llvm-config` 40 | option on `configure` to specify its location. 41 | 4. Run `make` to build the compiler. 42 | 5. Run `make check` to test the built compiler. 43 | 44 | ### Using CMake 45 | 1. Create a directory for the build. 46 | 2. Run `cmake ` to generate the build files. 47 | CMake will attempt to locate a usable copy of LLVM automatically, 48 | but you can specify -DLLVM_CONFIG= to force 49 | it to use a particular installation of LLVM. 50 | 3. Run `make` to build the compiler. 51 | 52 | Running the compiler 53 | -------------------- 54 | 55 | The build will produce a program called **blissc** in your build 56 | directory. Run `./blissc --help` for a description of the arguments 57 | and options. 58 | 59 | 60 | License 61 | ------- 62 | All sources are released under the BSD 2-clause license. See the 63 | [LICENSE.TXT](https://github.com/madisongh/blissc/blob/master/LICENSE.TXT) 64 | file for the license text. 65 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(blissc VERSION 0.5.0) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules") 6 | 7 | find_package(LLVM 14.0 REQUIRED) 8 | find_package(Python3 COMPONENTS Interpreter) 9 | 10 | include_directories(${blissc_BINARY_DIR}/include ${CMAKE_SOURCE_DIR}/include) 11 | 12 | if(NOT MSVC) 13 | add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wpedantic $<$:-Werror>) 14 | endif() 15 | 16 | add_library(libdriver STATIC lib/driver/driver.c) 17 | add_library(libfrontend STATIC 18 | lib/frontend/charfuncs.c 19 | lib/frontend/declarations.c 20 | lib/frontend/execfuncs.c 21 | lib/frontend/expr_control.c 22 | lib/frontend/expression.c 23 | lib/frontend/lexeme.c 24 | lib/frontend/lexer.c 25 | lib/frontend/libgen.c 26 | lib/frontend/listings.c 27 | lib/frontend/macros.c 28 | lib/frontend/nametable.c 29 | lib/frontend/parser.c 30 | lib/frontend/scanner.c 31 | lib/frontend/structures.c 32 | lib/frontend/switches.c 33 | lib/frontend/symbols.c) 34 | add_library(libllvmgen STATIC 35 | lib/llvmgen/llvm_ctrlexpgen.c 36 | lib/llvmgen/llvm_execfuncgen.c 37 | lib/llvmgen/llvm_expgen.c 38 | lib/llvmgen/llvm_gencode.c 39 | lib/llvmgen/llvm_machines.c 40 | lib/llvmgen/llvm_opexpgen.c 41 | lib/llvmgen/llvm_symgen.c 42 | lib/llvmgen/llvm_builtins_x86.c 43 | lib/llvmgen/llvm_helper.cpp) 44 | target_compile_options(libllvmgen 45 | PRIVATE $<$:-Wno-unknown-pragmas ${LLVM_CXXFLAGS}> 46 | PRIVATE $<$:${LLVM_CFLAGS}>) 47 | add_library(libsupport STATIC 48 | lib/support/fileio.c 49 | lib/support/logging.c 50 | lib/support/statcodes.c 51 | lib/support/strings.c 52 | lib/support/utils.c) 53 | add_executable(blissc driver/blissc.c) 54 | target_link_libraries(blissc 55 | libdriver 56 | libfrontend 57 | libsupport 58 | libllvmgen 59 | ${LLVM_LIBRARIES} 60 | ${LLVM_SYSTEM_LIBS}) 61 | target_link_options(blissc PUBLIC ${LLVM_LDFLAGS} -Wl,-rpath=${LLVM_LIBDIR} -Wl,-rpath-link=${LLVM_LIBDIR}) 62 | configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.in ${blissc_BINARY_DIR}/include/blissc/config.h @ONLY) 63 | 64 | if (${Python3_FOUND}) 65 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/tests) 66 | add_custom_target(testharness 67 | COMMAND $ --library -o ${CMAKE_BINARY_DIR}/tests/testharness.lib ${CMAKE_SOURCE_DIR}/tests/testharness.req 68 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests 69 | DEPENDS blissc) 70 | 71 | add_custom_target(check ALL 72 | COMMAND python3 "${CMAKE_SOURCE_DIR}/tests/runtests.py" --blissc=$ --cc="${CMAKE_C_COMPILER}" "${CMAKE_SOURCE_DIR}/tests" 73 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests 74 | DEPENDS testharness blissc) 75 | else() 76 | message(No Python3 - omitting compiler tests) 77 | endif() 78 | -------------------------------------------------------------------------------- /tests/lexical/macros.bli: -------------------------------------------------------------------------------- 1 | MODULE test = 2 | BEGIN 3 | 4 | LIBRARY 'testharness.lib'; 5 | 6 | COMPILETIME 7 | ctval = 0; 8 | 9 | MACRO 10 | nullmacro = %, 11 | macro_noparams = 1234 %, 12 | doswap(a, b) = (LOCAL tmp; tmp = .a; a = .b; b = .tmp)%, 13 | condmacro(x)[] = %STRING(x), condmacro(%REMAINING) %, 14 | test_cases(caseindex)[fmt, vals] = 15 | %ASSIGN(ctval, %COUNT+1) 16 | [ctval] : test_output(caseindex, fmt, %REMOVE(vals)) %, 17 | macro_argcount(x)[] = %LENGTH %, 18 | expandthis = %ASCIZ'expanded' %, 19 | expander(mname) = %EXPAND %QUOTE mname %, 20 | testlitname = hello%, 21 | test_exititer1[a] = %IF %COUNT EQL 2 %THEN a %EXITITERATION %FI '.' %, 22 | test_exititer[] = %ASCIZ %STRING(test_exititer1(%REMAINING)) %, 23 | test_exitmacro(x) = UPLIT(%ASCIZ %STRING( 'exit ', 24 | %IF x %THEN 'early')) %EXITMACRO %FI 'normally')) %, 25 | testunquote = %UNQUOTE testlitname %; 26 | 27 | KEYWORDMACRO 28 | copyvector(dest, src, n=1) = 29 | INCR i FROM 0 TO (n)-1 DO dest[.i] = .src[.i] %, 30 | test_errormacro(generror=0) = 31 | %IF generror %THEN %ERRORMACRO('%ERRORMACRO invoked') %FI %; 32 | 33 | LITERAL 34 | vecsize = 10, 35 | testunquote = 99; 36 | 37 | OWN 38 | vec1 : VECTOR [vecsize], 39 | vec2 : VECTOR [vecsize], 40 | yesno : VECTOR [2] INITIAL(UPLIT(%ASCIZ'NO'),UPLIT(%ASCIZ'YES')); 41 | 42 | test_setup(numcases=10); 43 | 44 | GLOBAL ROUTINE runtest (caseno) : NOVALUE = 45 | BEGIN 46 | LOCAL 47 | x, y, vecseql; 48 | 49 | IF .caseno EQL 1 THEN 50 | BEGIN 51 | x = 999; 52 | y = 888; 53 | doswap(x, y); 54 | test_output(.caseno, 'x=%d, y=%d', .x, .y); 55 | !! 1 x=888, y=999 56 | END 57 | ELSE IF .caseno EQL 2 THEN 58 | BEGIN 59 | INCR i FROM 0 TO vecsize-1 DO vec1[.i] = .i+1; 60 | copyvector(src=vec1, dest=vec2, n=vecsize); 61 | vecseql = (INCR i FROM 0 TO vecsize-1 DO 62 | IF .vec1[.i] NEQ .vec2[.i] THEN EXITLOOP 0; 1); 63 | test_output(.caseno, '%s', .yesno[.vecseql]); 64 | !! 2 YES 65 | END 66 | ELSE 67 | CASE .caseno-2 FROM 1 TO 8 OF 68 | test_cases(.caseno, 69 | 'nullmacro is null: %s', .yesno[%NULL(nullmacro)], 70 | !! 3 nullmacro is null: YES 71 | '%d', macro_noparams, 72 | !! 4 1234 73 | 'count=%d', macro_argcount(1,2,3,4,5), 74 | !! 5 count=5 75 | '%%EXPAND test: %s', UPLIT(expander(%QUOTE expandthis)), 76 | !! 6 %EXPAND test: expanded 77 | 'hello=%d', hello, 78 | !! 7 hello=99 79 | '"%s"', UPLIT(test_exititer(%EXPLODE('abcde'))), 80 | !! 8 "..c.." 81 | '"%s"', test_exitmacro(0), 82 | !! 9 "exit normally" 83 | '"%s"', test_exitmacro(1)); 84 | !! 10 "exit early" 85 | 86 | test_errormacro(generror=0) 87 | test_errormacro(generror=1) 88 | !! cerr %BLISS-E-USRERR, %ERRORMACRO invoked 89 | 90 | END; 91 | 92 | END 93 | ELUDOM 94 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Makefile for blissc 2 | # 3 | # Copyright (c) 2013-2020, Matthew Madison. 4 | # All rights reserved. 5 | # Distributed under license. See LICENSE.TXT for details. 6 | # 7 | 8 | AUTOMAKE_OPTIONS = subdir-objects foreign 9 | AM_CPPFLAGS = -Iinclude -I$(srcdir)/include -I$(LLVM_INCLUDEDIR) 10 | AM_CFLAGS = -Wall 11 | 12 | noinst_LIBRARIES = libdriver.a libfrontend.a libsupport.a libllvmgen.a 13 | libdriver_a_SOURCES = lib/driver/driver.c 14 | libfrontend_a_SOURCES = \ 15 | lib/frontend/charfuncs.c \ 16 | lib/frontend/declarations.c \ 17 | lib/frontend/execfuncs.c \ 18 | lib/frontend/expr_control.c \ 19 | lib/frontend/expression.c \ 20 | lib/frontend/lexeme.c \ 21 | lib/frontend/lexer.c \ 22 | lib/frontend/libgen.c \ 23 | lib/frontend/listings.c \ 24 | lib/frontend/macros.c \ 25 | lib/frontend/nametable.c \ 26 | lib/frontend/parser.c \ 27 | lib/frontend/scanner.c \ 28 | lib/frontend/structures.c \ 29 | lib/frontend/switches.c \ 30 | lib/frontend/symbols.c 31 | libllvmgen_a_SOURCES = \ 32 | lib/llvmgen/llvm_ctrlexpgen.c \ 33 | lib/llvmgen/llvm_execfuncgen.c \ 34 | lib/llvmgen/llvm_expgen.c \ 35 | lib/llvmgen/llvm_gencode.c \ 36 | lib/llvmgen/llvm_machines.c \ 37 | lib/llvmgen/llvm_opexpgen.c \ 38 | lib/llvmgen/llvm_symgen.c \ 39 | lib/llvmgen/llvm_builtins_x86.c \ 40 | lib/llvmgen/llvm_helper.cpp 41 | 42 | libllvmgen_a_CFLAGS = $(LLVM_CFLAGS) 43 | libllvmgen_a_CXXFLAGS = -Wno-unknown-pragmas $(LLVM_CXXFLAGS) 44 | 45 | libsupport_a_SOURCES = \ 46 | lib/support/fileio.c \ 47 | lib/support/logging.c \ 48 | lib/support/statcodes.c \ 49 | lib/support/strings.c \ 50 | lib/support/utils.c 51 | 52 | bin_PROGRAMS = blissc 53 | blissc_SOURCES = driver/blissc.c 54 | blissc_LDADD = $(noinst_LIBRARIES) $(LLVM_LIBS) -lstdc++ $(LLVM_SYSLIBS) $(LLVM_LDFLAGS) 55 | blissc_LDFLAGS = -L$(LLVM_LIBDIR) -Wl,-rpath=$(LLVM_LIBDIR) -Wl,-rpath-link=$(LLVM_LIBDIR) 56 | 57 | SUBDIRS = tests 58 | DIST_SUBDIRS = 59 | EXTRA_DIST = \ 60 | LICENSE.TXT README.md \ 61 | include/blissc/charfuncs.h \ 62 | include/blissc/declarations.h \ 63 | include/blissc/driver.h \ 64 | include/blissc/execfuncs.h \ 65 | include/blissc/expression.h \ 66 | include/blissc/gencode.h \ 67 | include/blissc/lexeme.h \ 68 | include/blissc/lexer.h \ 69 | include/blissc/libgen.h \ 70 | include/blissc/listings.h \ 71 | include/blissc/machinedef.h \ 72 | include/blissc/macros.h \ 73 | include/blissc/nametable.h \ 74 | include/blissc/parser.h \ 75 | include/blissc/structures.h \ 76 | include/blissc/switches.h \ 77 | include/blissc/symbols.h \ 78 | include/blissc/ir/blissir.h \ 79 | include/blissc/ir/instructions.h \ 80 | include/blissc/support/fileio.h \ 81 | include/blissc/support/logging.h \ 82 | include/blissc/support/statcodes.h \ 83 | include/blissc/support/strings.h \ 84 | include/blissc/support/utils.h \ 85 | lib/frontend/scanner.h \ 86 | lib/llvmgen/llvmgen.h \ 87 | lib/llvmgen/llvm_helper.h \ 88 | lib/llvmgen/llvm_machinectx.h \ 89 | tests/runtests.py tests/testharness.req tests/testharness.c \ 90 | tests/expression tests/lexical 91 | -------------------------------------------------------------------------------- /include/blissc/machinedef.h: -------------------------------------------------------------------------------- 1 | #ifndef machinedef_h__ 2 | #define machinedef_h__ 3 | /* 4 | *++ 5 | * machinedef.h - Machine-specific definitions. 6 | * 7 | * Copyright © 2012, 2013-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include 13 | 14 | #define MACH_M_ADDRS_SIGNED (1<<0) 15 | #define MACH_M_SIGNEXT (1<<1) 16 | #define MACH_M_LTC_INIT (1<<2) 17 | 18 | #define MACH_K_MAXCSCOUNT 4 19 | 20 | typedef enum { 21 | MACH_K_OUTPUT_ASM, 22 | MACH_K_OUTPUT_OBJ 23 | } machine_output_t; 24 | 25 | struct machine_ctx_s; 26 | typedef struct machine_ctx_s *machine_ctx_t; 27 | 28 | struct machinedef_s { 29 | machine_ctx_t machctx; 30 | unsigned int bpunit, bpval, bpaddr; 31 | unsigned int charsize_count; 32 | unsigned int charsizes[MACH_K_MAXCSCOUNT]; 33 | unsigned int max_align; // maximum # of bits for ALIGN() attribute 34 | unsigned int reg_count; 35 | unsigned int flags; 36 | }; 37 | 38 | typedef struct machinedef_s machinedef_t; 39 | 40 | #define siu static inline __attribute__((unused)) 41 | siu unsigned int machine_unit_bits(machinedef_t *mach) { return mach->bpunit; } 42 | siu unsigned int machine_scalar_bits(machinedef_t *mach) { return mach->bpval; } 43 | siu unsigned int machine_scalar_units(machinedef_t *mach) { return mach->bpval/mach->bpunit; } 44 | siu unsigned int machine_addr_bits(machinedef_t *mach) { return mach->bpaddr; } 45 | siu unsigned int machine_addr_units(machinedef_t *mach) { return mach->bpaddr/mach->bpunit; } 46 | siu int machine_addr_signed(machinedef_t *mach) { return (mach->flags & MACH_M_ADDRS_SIGNED) != 0; } 47 | siu int machine_signext_supported(machinedef_t *mach) { return (mach->flags & MACH_M_SIGNEXT) != 0; } 48 | // The following for host compiler to target machine mapping 49 | siu unsigned int machine_unit_maxbytes(machinedef_t *mach) { return mach->bpunit/8; } 50 | siu unsigned int machine_scalar_maxbytes(machinedef_t *mach) { return mach->bpval/8; } 51 | siu unsigned int machine_addr_maxbytes(machinedef_t *mach) { return mach->bpaddr/8; } 52 | siu unsigned int machine_register_count(machinedef_t *mach) { return mach->reg_count; } 53 | siu unsigned int machine_align_max(machinedef_t *mach) { return mach->max_align; } 54 | siu int machine_linktime_constant_initializers(machinedef_t *mach) { 55 | return (mach->flags & MACH_M_LTC_INIT) != 0; } 56 | siu machine_ctx_t machine_context(machinedef_t *mach) { return mach->machctx; } 57 | siu unsigned int machine_cs_count(machinedef_t *mach) { return mach->charsize_count; } 58 | siu unsigned int machine_charsize(machinedef_t *mach, int idx) { return mach->charsizes[idx]; } 59 | #undef siu 60 | 61 | machinedef_t *machine_init(const char *machspec); 62 | void machine_finish(machinedef_t *mach); 63 | void machine_psects_init(machinedef_t *mach, void *scope); 64 | void machine_output_set(machinedef_t *mach, machine_output_t outtype, 65 | char *fname, size_t fnlen); 66 | void machine_dumpir_set(machinedef_t *mach, char *fname, size_t fnlen); 67 | const char *machine_triple(machinedef_t *mach); 68 | 69 | #endif /* machinedef_h__ */ 70 | -------------------------------------------------------------------------------- /cmake/Modules/FindLLVM.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2024, Matthew Madison 2 | # Distributed under license. See LICENSE.TXT for details. 3 | 4 | #.rst: 5 | #FindLLVM 6 | #-------- 7 | # 8 | # Finds LLVM 9 | # 10 | # This will define the following variables: 11 | # 12 | # LLVM_FOUND - True if LLVM is found 13 | # LLVM_VERSION_STRING - The version of LLVM which was found 14 | # LLVM_HOST_TRIPLE - The host triple LLVM was built with 15 | # LLVM_CFLAGS - Compiler options for C modules calling LLVM 16 | # LLVM_CXXFLAGS - Compiler options for C++ modules calling LLVM 17 | # LLVM_INCLUDE_DIRS - Include directories for LLVM 18 | # LLVM_LDFLAGS - Linker options for linking with LLVM 19 | # LLVM_LIBDIR - Path to the LLVM libraries directory 20 | # LLVM_LIBRARIES - Libraries to include when linking with LLVM 21 | # LLVM_SYSTEM_LIBS - System libraries to link with when using LLVM 22 | # 23 | # Variables that control where to look: 24 | # 25 | # LLVM_CONFIG - pathname of a working llvm-config program 26 | 27 | if(NOT LLVM_CONFIG) 28 | string(REGEX REPLACE "([0-9]+)\\..*" "\\1" _llvm_find_version_major "${LLVM_FIND_VERSION}") 29 | find_program(LLVM_CONFIG NAMES llvm-config-${_llvm_find_version_major} llvm-config) 30 | endif() 31 | 32 | macro(_llvm_fail _msg) 33 | if(LLVM_FIND_REQUIRED) 34 | message(FATAL_ERROR "${_msg}") 35 | else() 36 | if(NOT LLVM_FIND_QUIETLY) 37 | message(STATUS "${_msg}") 38 | endif() 39 | endif() 40 | endmacro() 41 | 42 | if(NOT LLVM_CONFIG) 43 | if(NOT LLVM_FIND_QUIETLY) 44 | message(WARNING "Could not locate llvm-config executable. Set LLVM_CONFIG to point to a working version of that program.") 45 | endif() 46 | else() 47 | macro(_llvm_set _var _flag _type _doc) 48 | if(LLVM_FIND_QUIETLY) 49 | set(_quiet ERROR_QUIET) 50 | else() 51 | set(_quiet) 52 | endif() 53 | set(_result) 54 | execute_process( 55 | COMMAND ${LLVM_CONFIG} --${_flag} 56 | RESULT_VARIABLE _result 57 | OUTPUT_VARIABLE _tmp 58 | OUTPUT_STRIP_TRAILING_WHITESPACE 59 | ${_quiet}) 60 | if(_result) 61 | _llvm_fail("llvm-config failed, result: ${_result}") 62 | endif() 63 | separate_arguments(_tmp) 64 | set(LLVM_${_var} ${_tmp} CACHE ${_type} ${_doc}) 65 | endmacro() 66 | _llvm_set(VERSION_STRING version STRING "LLVM version") 67 | _llvm_set(CFLAGS cflags STRING "LLVM CFLAGS") 68 | _llvm_set(CXXFLAGS cxxflags STRING "LLVM CXXFLAGS") 69 | _llvm_set(INCLUDE_DIRS includedir PATH "LLVM include directories") 70 | _llvm_set(LDFLAGS ldflags STRING "LLVM LDFLAGS") 71 | _llvm_set(SYSTEM_LIBS system-libs STRING "LLVM system libraies") 72 | _llvm_set(LIBDIR libdir PATH "LLVM library directory") 73 | _llvm_set(LIBRARIES libs STRING "LLVM libraries") 74 | _llvm_set(HOST_TRIPLE host-target STRING "LLVM host triple") 75 | endif() 76 | 77 | if(${LLVM_VERSION_STRING} VERSION_LESS ${LLVM_FIND_VERSION}) 78 | message(FATAL_ERROR "Found version ${LLVM_VERSION_STRING} of LLVM, need at least ${LLVM_FIND_VERSION}") 79 | endif() 80 | 81 | string(REGEX REPLACE "([0-9]+)\\..*" "\\1" LLVM_VERSION_MAJOR "${LLVM_VERSION_STRING}") 82 | string(REGEX REPLACE "[0.9]+\\.([0-9]+).*" "\\1" LLVM_VERSION_MINOR "${LLVM_VERSION_STRING}") 83 | 84 | include(FindPackageHandleStandardArgs) 85 | find_package_handle_standard_args(LLVM 86 | REQUIRED_VARS LLVM_LIBRARIES 87 | VERSION_VAR LLVM_VERSION_STRING) 88 | -------------------------------------------------------------------------------- /include/blissc/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef parser_h__ 2 | #define parser_h__ 3 | /* 4 | *++ 5 | * parser.h - Parser definitions. 6 | * 7 | * Copyright © 2012-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include "nametable.h" 14 | #include "lexeme.h" 15 | #include "scanner.h" 16 | #include "listings.h" 17 | #include "machinedef.h" 18 | #include "support/logging.h" 19 | #include "support/utils.h" 20 | 21 | typedef enum { 22 | PUNCT_COMMASEP_NOGROUP, 23 | PUNCT_SEMISEP_NOGROUP, 24 | PUNCT_OPERSEP_NOGROUP, 25 | PUNCT_SEMISEP_SETTES, 26 | PUNCT_COMMASEP_PARENS 27 | } punctclass_t; 28 | 29 | struct parse_ctx_s; 30 | typedef struct parse_ctx_s *parse_ctx_t; 31 | 32 | typedef int (*lexfunc_t)(parse_ctx_t pctx, void *ctx, quotelevel_t ql, 33 | lextype_t curlt); 34 | parse_ctx_t parser_init(strctx_t strctx, namectx_t namectx, machinedef_t *mach, 35 | scopectx_t *kwdscopep, logctx_t logctx, void *fioctx); 36 | void parser_lexfunc_register(parse_ctx_t pctx, void *ctx, 37 | lextype_t lt, lexfunc_t fn); 38 | int parser_fopen_main(parse_ctx_t pctx, const char *fname, size_t fnlen, 39 | unsigned int listopts, const char *listfname, size_t lfnlen); 40 | int parser_fopen(parse_ctx_t pctx, const char *fname, size_t fnlen, 41 | char **actnamep); 42 | int parser_popen(parse_ctx_t pctx, scan_input_fn infn, void *fnctx); 43 | int parser_lib_process(parse_ctx_t pctx, strdesc_t *libname); 44 | void parser_finish(parse_ctx_t pctx); 45 | lexctx_t parser_lexmemctx(parse_ctx_t pctx); 46 | logctx_t parser_logctx(parse_ctx_t pctx); 47 | lextype_t parser_next(parse_ctx_t pctx, quotelevel_t ql, lexeme_t **lex); 48 | void parser_insert(parse_ctx_t pctx, lexeme_t *lex); 49 | void parser_insert_seq(parse_ctx_t pctx, lexseq_t *seq); 50 | void parser_skip_to_delim(parse_ctx_t pctx, lextype_t delimtype); 51 | scopectx_t parser_scope_get(parse_ctx_t pctx); 52 | scopectx_t parser_scope_push(parse_ctx_t pctx, scopectx_t newscope); 53 | scopectx_t parser_scope_pop(parse_ctx_t pctx); 54 | scopectx_t parser_scope_begin(parse_ctx_t pctx); 55 | scopectx_t parser_scope_end(parse_ctx_t pctx); 56 | void parser_incr_erroneof(parse_ctx_t pctx); 57 | void parser_decr_erroneof(parse_ctx_t pctx); 58 | void parser_skipmode_set(parse_ctx_t pctx, int val); 59 | int parser_condstate_push(parse_ctx_t pctx, condstate_t newcs); 60 | condstate_t parser_condstate_get(parse_ctx_t pctx); 61 | int parse_lexeme_seq(parse_ctx_t pctx, lexseq_t *seq, quotelevel_t ql, 62 | lextype_t terms[], int nterms, 63 | lexseq_t *result, lextype_t *term); 64 | lexeme_t *parser_lexeme_create(parse_ctx_t pctx, lextype_t lt, strdesc_t *txt); 65 | int parser_expect(parse_ctx_t pctx, quotelevel_t ql, 66 | lextype_t explt, lexeme_t **lex, int pboe); 67 | int parser_expect_oneof(parse_ctx_t pctx, quotelevel_t ql, 68 | lextype_t explts[], int numlts, 69 | lexeme_t **lex, int pboe); 70 | machinedef_t *parser_get_machinedef(parse_ctx_t pctx); 71 | strctx_t parser_strctx(parse_ctx_t pctx); 72 | textpos_t parser_curpos(parse_ctx_t pctx); 73 | scopectx_t parser_kwdscope(parse_ctx_t pctx); 74 | lstgctx_t parser_lstgctx(parse_ctx_t pctx); 75 | void parser_punctclass_set(parse_ctx_t pctx, punctclass_t cl, lextype_t lt); 76 | void parser_punctclass_get(parse_ctx_t pctx, punctclass_t *clp, lextype_t *ltp); 77 | lexeme_t *parser_punct_grouper(parse_ctx_t pctx, int docloser); 78 | lexeme_t *parser_punct_separator(parse_ctx_t pctx); 79 | lexeme_t *parse_string_params(parse_ctx_t pctx, int openparenparsed); 80 | void parser_variant_set(parse_ctx_t pctx, unsigned int val); 81 | int parser_atend(parse_ctx_t pctx); 82 | void parser_compilerinfo_set(parse_ctx_t pctx, compilerinfo_t *ci); 83 | int parser_searchpath_add(parse_ctx_t pctx, strdesc_t *path); 84 | unsigned int parser_searchpathcount_get(parse_ctx_t pctx); 85 | strdesc_t *parser_searchpath_get(parse_ctx_t pctx, unsigned int i); 86 | #endif /* parser_h__ */ 87 | -------------------------------------------------------------------------------- /lib/llvmgen/llvm_machines.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * llvm_machines.c - Maps LLVM targets to BLISS machines. 4 | * 5 | * 6 | * Manages the machine-specific context for a compilation, 7 | * mapping it to the appropriate LLVM target. 8 | * 9 | * XXX Currently only handles one target machine. 10 | * 11 | * Copyright © 2013-2024, Matthew Madison. 12 | * All rights reserved. 13 | * Distributed under license. See LICENSE.TXT for details. 14 | *-- 15 | */ 16 | 17 | #include "llvm_machinectx.h" 18 | #include "llvm_helper.h" 19 | #include 20 | #include 21 | 22 | /* 23 | * machine_init 24 | * 25 | * Initializes the machine context. 26 | */ 27 | machinedef_t * 28 | machine_init (const char *mspec) 29 | { 30 | machine_ctx_t m; 31 | machinedef_t *mach; 32 | char *err; 33 | LLVMTargetRef target; 34 | const char *machspec = mspec; 35 | unsigned long allosize; 36 | 37 | if (machspec == 0) { 38 | machspec = HelperGetDefaultTriple(); 39 | } 40 | allosize = (sizeof(struct machine_ctx_s) + 41 | sizeof(struct machinedef_s) + 42 | strlen(machspec) + 1); 43 | m = malloc(allosize); 44 | if (m == 0) return 0; 45 | memset(m, 0, allosize); 46 | m->triple = ((char *) m) + (sizeof(struct machine_ctx_s) + 47 | sizeof(struct machinedef_s)); 48 | memcpy(m->triple, machspec, strlen(machspec)); 49 | 50 | LLVM_NATIVE_TARGETINFO(); 51 | LLVM_NATIVE_TARGET(); 52 | LLVM_NATIVE_TARGETMC(); 53 | LLVM_NATIVE_ASMPRINTER(); 54 | LLVM_NATIVE_ASMPARSER(); 55 | 56 | err = 0; 57 | target = HelperLookupTarget(machspec, &err); 58 | if (target == 0) { 59 | if (err != 0) free(err); 60 | free(m); 61 | return 0; 62 | } 63 | m->target_machine = LLVMCreateTargetMachine(target, machspec, "", "", 64 | LLVMCodeGenLevelDefault, 65 | LLVMRelocPIC, LLVMCodeModelDefault); 66 | if (m->target_machine == 0) { 67 | free(m); 68 | return 0; 69 | } 70 | HelperSetAsmVerbosity(m->target_machine, 1); 71 | m->llvmctx = LLVMContextCreate(); 72 | if (m->llvmctx == 0) { 73 | LLVMDisposeTargetMachine(m->target_machine); 74 | free(m); 75 | return 0; 76 | } 77 | 78 | m->is_macho = (strstr(machspec, "darwin") != 0); // XXX 79 | 80 | mach = (machinedef_t *)(m + 1); 81 | 82 | mach->machctx = m; 83 | mach->bpaddr = sizeof(int *) * 8; 84 | mach->bpval = sizeof(long) * 8; 85 | mach->bpunit = 8; 86 | mach->charsize_count = 1; 87 | mach->charsizes[0] = 8; 88 | mach->flags = MACH_M_SIGNEXT | MACH_M_LTC_INIT; 89 | mach->max_align = 4; 90 | mach->reg_count = 16; 91 | 92 | return mach; 93 | 94 | } /* machine_init */ 95 | 96 | /* 97 | * machine_triple 98 | * 99 | */ 100 | const char * 101 | machine_triple (machinedef_t *mach) 102 | { 103 | machine_ctx_t m = machine_context(mach); 104 | return m->triple; 105 | 106 | } /* machine_triple */ 107 | 108 | /* 109 | * machine_output_set 110 | * 111 | * Called by the driver to set the output type and output filename. 112 | */ 113 | void 114 | machine_output_set (machinedef_t *mach, machine_output_t outtype, char *fname, size_t fnlen) 115 | { 116 | machine_ctx_t m = machine_context(mach); 117 | 118 | m->outputtype = (outtype == MACH_K_OUTPUT_ASM ? LLVMAssemblyFile : LLVMObjectFile); 119 | m->outfile = malloc(fnlen+1); 120 | memcpy(m->outfile, fname, fnlen); 121 | m->outfile[fnlen] = '\0'; 122 | 123 | } /* machine_output_set */ 124 | 125 | /* 126 | * machine_dumpir_set 127 | * 128 | * Called by the driver when an IR dump is requested. 129 | */ 130 | void 131 | machine_dumpir_set (machinedef_t *mach, char *fname, size_t fnlen) 132 | { 133 | machine_ctx_t m = machine_context(mach); 134 | 135 | if (m->irdumpfile != 0) { 136 | free(m->irdumpfile); 137 | m->irdumpfile = 0; 138 | } 139 | if (fname != 0 && fnlen != 0) { 140 | m->irdumpfile = malloc(fnlen+1); 141 | memcpy(m->irdumpfile, fname, fnlen); 142 | m->irdumpfile[fnlen] = '\0'; 143 | } 144 | 145 | } /* machine_dumpir_set */ 146 | 147 | /* 148 | * machine_finish 149 | * 150 | * Context cleanup. 151 | */ 152 | void 153 | machine_finish (machinedef_t *mach) 154 | { 155 | machine_ctx_t m; 156 | 157 | if (mach == 0) return; 158 | m = machine_context(mach); 159 | if (m == 0) return; 160 | 161 | if (m->target_machine != 0) LLVMDisposeTargetMachine(m->target_machine); 162 | if (m->llvmctx != 0) LLVMContextDispose(m->llvmctx); 163 | if (m->outfile != 0) free(m->outfile); 164 | free(m); 165 | 166 | } /* machine_finish */ 167 | -------------------------------------------------------------------------------- /lib/support/statcodes.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * statcodes.c - Status codes and messages 4 | * 5 | * This module implements the message formatting 6 | * parts of the status code facility. 7 | * 8 | * Copyright © 2012-2020, Matthew Madison. 9 | * All rights reserved. 10 | * Distributed under license. See LICENSE.TXT for details. 11 | *-- 12 | */ 13 | #include 14 | #include 15 | #include 16 | #define STATCODES_INSTANTIATE 17 | #include "blissc/support/statcodes.h" 18 | #include "blissc/support/strings.h" 19 | 20 | #define STATCODE(msg,typ,nam,txt) #nam, 21 | static const char *mcnames[] = { 22 | STATCODES 23 | }; 24 | #undef STATCODE 25 | 26 | #define STATCODE(msg,typ,nam,txt) txt, 27 | static const char *mctext[] = { 28 | STATCODES 29 | }; 30 | #undef STATCODE 31 | 32 | #define MSG_COUNT (sizeof(mctext)/sizeof(mctext[0])) 33 | static const char sevchars[8] = {'?', 'S', 'I', '?', '?', 'W', 'E', 'F'}; 34 | 35 | /* 36 | * stc_msg_format 37 | * 38 | * Message formatting, with variable-length arg list. 39 | */ 40 | int 41 | stc_msg_format (statcode_t statcode, char *buf, size_t bufsiz, ...) 42 | { 43 | va_list ap; 44 | int result; 45 | 46 | va_start(ap, bufsiz); 47 | result = stc_msg_vformat(statcode, buf, bufsiz, ap); 48 | va_end(ap); 49 | return result; 50 | 51 | } /* stc_msg_format */ 52 | 53 | /* 54 | * stc_msg_vformat 55 | * 56 | * The core formatting routine. 57 | * Do NOT use sprintf-style formatting codes in 58 | * message strings! Use the formatting directives 59 | * below: 60 | * !SL = text with length (char *str, int len) 61 | * !SZ = null-terminated string (char *azstr) 62 | * !SD = string descriptor (strdesc_t *sdsc) 63 | * !U{I,S,L} = unsigned int, short, long 64 | * !I{I,S,L} = signed int, short, long 65 | * !X{I,S,L} = hexadecimal int, short, long 66 | * !P = pointer 67 | * !! = exclamation point 68 | */ 69 | int 70 | stc_msg_vformat (statcode_t statcode, char *buf, size_t bufsiz, va_list ap) 71 | { 72 | int len; 73 | char tmpbuf[32]; 74 | char *outp; 75 | const char *fmt; 76 | unsigned int msgno = stc_msgno(statcode); 77 | unsigned int sev = stc_severity(statcode); 78 | 79 | if (msgno >= MSG_COUNT) { 80 | return snprintf(buf, bufsiz, "%%BLISS-%c-UNKNOWN: unknown message code %u", 81 | sevchars[sev], msgno); 82 | } 83 | 84 | len = snprintf(buf, bufsiz, "%%BLISS-%c-%s, ", sevchars[sev], mcnames[msgno]); 85 | if (len >= (int) bufsiz) { len = (int) bufsiz - 1; } 86 | outp = buf + len; 87 | bufsiz -= len; 88 | 89 | fmt = mctext[msgno]; 90 | 91 | while (bufsiz > 0 && *fmt != '\0') { 92 | if (*fmt != '!') { 93 | *outp++ = *fmt++; 94 | bufsiz -= 1; 95 | continue; 96 | } 97 | fmt += 1; 98 | if (*fmt == '\0') break; 99 | if (*fmt == '!') { 100 | *outp++ = *fmt++; 101 | bufsiz -= 1; 102 | continue; 103 | } 104 | if (*fmt == 'U' || *fmt == 'X') { 105 | unsigned long val; 106 | if (*(fmt+1) == 'L') { 107 | val = va_arg(ap, unsigned long); 108 | } else if (*(fmt+1) == 'S' || *(fmt+1) == 'I') { 109 | val = va_arg(ap, unsigned int); 110 | } else continue; 111 | len = snprintf(tmpbuf, sizeof(tmpbuf), 112 | (*fmt == 'U' ? "%lu" : "%lX"), val); 113 | if (len < 0) { len = 0; } 114 | if (len >= (int) bufsiz) { len = (int) bufsiz-1; } 115 | memcpy(outp, tmpbuf, (size_t) len); 116 | outp += len; 117 | bufsiz -= len; 118 | fmt += 2; 119 | continue; 120 | } 121 | if (*fmt == 'I') { 122 | long val; 123 | if (*(fmt+1) == 'L') { 124 | val = va_arg(ap, long); 125 | } else if (*(fmt+1) == 'S' || *(fmt+1) == 'I') { 126 | val = va_arg(ap, int); 127 | } else continue; 128 | len = snprintf(tmpbuf, sizeof(tmpbuf), "%ld", val); 129 | if (len < 0) { len = 0; } 130 | if (len >= (int) bufsiz) { len = (int) bufsiz-1; } 131 | memcpy(outp, tmpbuf, (size_t) len); 132 | outp += len; 133 | bufsiz -= len; 134 | fmt += 2; 135 | continue; 136 | } 137 | if (*fmt == 'P') { 138 | len = snprintf(tmpbuf, sizeof(tmpbuf), "%p", va_arg(ap, void *)); 139 | if (len < 0) { len = 0; } 140 | if (len >= (int) bufsiz) { len = (int) bufsiz-1; } 141 | memcpy(outp, tmpbuf, (size_t) len); 142 | outp += len; 143 | bufsiz -= len; 144 | fmt += 1; 145 | continue; 146 | } 147 | if (*fmt == 'S') { 148 | unsigned int slen; 149 | char *ptr; 150 | if (*(fmt+1) == 'L') { 151 | ptr = va_arg(ap, char *); 152 | slen = va_arg(ap, unsigned int); 153 | } else if (*(fmt+1) == 'Z') { 154 | ptr = va_arg(ap, char *); 155 | slen = (unsigned int) strlen(ptr); 156 | } else if (*(fmt+1) == 'D') { 157 | strdesc_t *dsc = va_arg(ap, strdesc_t *); 158 | ptr = dsc->ptr; 159 | slen = (unsigned int) dsc->len; 160 | } else continue; 161 | if (slen >= (unsigned int) bufsiz) { slen = (unsigned int) bufsiz - 1; } 162 | memcpy(outp, ptr, slen); 163 | outp += slen; 164 | bufsiz -= slen; 165 | fmt += 2; 166 | continue; 167 | } 168 | // Unrecognized directive here, just loop back up to the top 169 | } /* while bufsiz > 0 && *fmt != null char */ 170 | 171 | return (int) (outp - buf); 172 | 173 | } /* stc_msg_vformat */ 174 | -------------------------------------------------------------------------------- /lib/frontend/libgen.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * libgen.c - Library generation 4 | * 5 | * This module handles library file creation and 6 | * the reading of a library file's header. Serialization 7 | * and deserialization of declared names are handled in 8 | * in the nametables module. 9 | * 10 | * Section 16.6 of the LRM describes BLISS libraries, including 11 | * the types of declarations that may be present in a library 12 | * source file. 13 | * 14 | * XXX 15 | * In the current implementation, the compiler version, host 16 | * triple, and target triple must match exactly between the 17 | * compiler generating a library and the one reading it. 18 | * Some versioning and backward compatibility mechanism would 19 | * be nice to have, as would allowing libraries generated on 20 | * one host to be readable by another (for the same target). 21 | * XXX 22 | * 23 | * Copyright © 2013-2024, Matthew Madison. 24 | * All rights reserved. 25 | * Distributed under license. See LICENSE.TXT for details. 26 | *-- 27 | */ 28 | #include 29 | #include 30 | #include "blissc/libgen.h" 31 | #include "blissc/declarations.h" 32 | 33 | struct libgen_ctx_s { 34 | fioctx_t fioctx; 35 | filectx_t file; 36 | char *libname; 37 | compilerinfo_t compilerinfo; 38 | }; 39 | 40 | #define LIB_HEADER_VERSION 1 41 | 42 | /* 43 | * write_header 44 | * 45 | * Writes a library header. The header is written such that 46 | * it can be read on a different type of host (e.g., big vs. 47 | * little endian). 48 | */ 49 | static int 50 | write_header (libgen_ctx_t lgctx, const char *target_triple) 51 | { 52 | unsigned char hdrbuf[8]; 53 | size_t htlen = strlen(lgctx->compilerinfo.host_triple); 54 | size_t ttlen = strlen(target_triple); 55 | 56 | if (htlen >= 255) htlen = 255; 57 | if (ttlen >= 255) ttlen = 255; 58 | 59 | hdrbuf[0] = 'B'; 60 | hdrbuf[1] = 'L'; 61 | hdrbuf[2] = 'B'; 62 | hdrbuf[3] = LIB_HEADER_VERSION; 63 | hdrbuf[4] = (unsigned char) lgctx->compilerinfo.ver_major; 64 | hdrbuf[5] = (unsigned char) lgctx->compilerinfo.ver_minor; 65 | hdrbuf[6] = (unsigned char) htlen; 66 | hdrbuf[7] = (unsigned char) ttlen; 67 | if (file_writebuf(lgctx->file, hdrbuf, sizeof(hdrbuf)) < 0) { 68 | return -1; 69 | } 70 | if (file_writebuf(lgctx->file, lgctx->compilerinfo.host_triple, htlen) < 0) { 71 | return -1; 72 | } 73 | return file_writebuf(lgctx->file, target_triple, ttlen); 74 | 75 | } /* write_header */ 76 | 77 | /* 78 | * lib_parse_header 79 | * 80 | * Check that the input stream is a valid library file by 81 | * checking it against the version, host triple, and target triple. 82 | */ 83 | int 84 | lib_parse_header (logctx_t logctx, textpos_t curpos, 85 | filectx_t fh, compilerinfo_t *me, machinedef_t *mach) 86 | { 87 | const char *target = machine_triple(mach); 88 | unsigned char hdrbuf[8]; 89 | char buf[256]; 90 | size_t len; 91 | 92 | if (file_readbuf(fh, hdrbuf, sizeof(hdrbuf), &len) <= 0 || 93 | len != sizeof(hdrbuf)) { 94 | return 0; 95 | } 96 | if (hdrbuf[0] != 'B' || hdrbuf[1] != 'L' || hdrbuf[2] != 'B' 97 | || hdrbuf[3] != LIB_HEADER_VERSION) { 98 | log_signal(logctx, curpos, STC__INVLIBHDR, file_getname(fh)); 99 | return 0; 100 | } 101 | if (hdrbuf[4] != me->ver_major || hdrbuf[5] != me->ver_minor) { 102 | log_signal(logctx, curpos, STC__LIBVERMISM, file_getname(fh)); 103 | return 0; 104 | } 105 | if (hdrbuf[6] != strlen(me->host_triple)) { 106 | log_signal(logctx, curpos, STC__LIBHSTMISM, file_getname(fh)); 107 | return 0; 108 | } 109 | if (hdrbuf[7] != strlen(target)) { 110 | log_signal(logctx, curpos, STC__LIBTRGMISM, file_getname(fh)); 111 | return 0; 112 | } 113 | if (file_readbuf(fh, buf, hdrbuf[6], &len) <= 0 || 114 | len != hdrbuf[6]) { 115 | log_signal(logctx, curpos, STC__LIBRDERR, file_getname(fh)); 116 | return 0; 117 | } 118 | if (memcmp(buf, me->host_triple, len) != 0) { 119 | log_signal(logctx, curpos, STC__LIBHSTMISM, file_getname(fh)); 120 | return 0; 121 | } 122 | if (file_readbuf(fh, buf, hdrbuf[7], &len) <= 0 || 123 | len != hdrbuf[7]) { 124 | log_signal(logctx, curpos, STC__LIBRDERR, file_getname(fh)); 125 | return 0; 126 | } 127 | if (memcmp(buf, target, len) != 0) { 128 | log_signal(logctx, curpos, STC__LIBTRGMISM, file_getname(fh)); 129 | return 0; 130 | } 131 | return 1; 132 | 133 | } /* lib_parse_header */ 134 | 135 | /* 136 | * libgen_init 137 | * 138 | * Initialize library generation, setting the name of the 139 | * output file. 140 | */ 141 | libgen_ctx_t 142 | libgen_init (fioctx_t fioctx, const char *libname, size_t lnlen, 143 | compilerinfo_t *info) 144 | { 145 | libgen_ctx_t ctx; 146 | 147 | ctx = malloc(sizeof(struct libgen_ctx_s)+(lnlen+1)); 148 | if (ctx == 0) { 149 | return 0; 150 | } 151 | memset(ctx, 0, sizeof(struct libgen_ctx_s)); 152 | ctx->fioctx = fioctx; 153 | memcpy(&ctx->compilerinfo, info, sizeof(compilerinfo_t)); 154 | ctx->libname = ((char *)ctx) + sizeof(struct libgen_ctx_s); 155 | memcpy(ctx->libname, libname, lnlen); 156 | ctx->libname[lnlen] = '\0'; 157 | 158 | return ctx; 159 | 160 | } /* libgen_init */ 161 | 162 | /* 163 | * libgen_parse 164 | * 165 | * Parse declarations and call scope_serialize() to generate 166 | * the library. 167 | */ 168 | int 169 | libgen_parse (libgen_ctx_t lgctx, void *vctx) 170 | { 171 | expr_ctx_t ctx = vctx; 172 | scopectx_t scope = parser_scope_get(expr_parse_ctx(ctx)); 173 | 174 | expr_libgen_set(ctx, 1); 175 | parse_libgen_declarations(ctx); 176 | 177 | lgctx->file = file_open_output(lgctx->fioctx, lgctx->libname, strlen(lgctx->libname)); 178 | if (lgctx->file == 0) { 179 | return 0; 180 | } 181 | if (write_header(lgctx, machine_triple(expr_machinedef(ctx))) < 0) { 182 | return 0; 183 | } 184 | scope_serialize(scope, lgctx->file, 0); 185 | file_close(lgctx->file); 186 | 187 | return 1; 188 | 189 | } /* libgen_parse */ 190 | 191 | /* 192 | * libgen_finish 193 | * 194 | * Module cleanup. 195 | */ 196 | void 197 | libgen_finish (libgen_ctx_t lgctx) 198 | { 199 | if (lgctx != 0) free(lgctx); 200 | 201 | } /* libgen_finish */ 202 | -------------------------------------------------------------------------------- /include/blissc/nametable.h: -------------------------------------------------------------------------------- 1 | #ifndef nametable_h__ 2 | #define nametable_h__ 3 | /* 4 | *++ 5 | * nametable.h - Name table definitions. 6 | * 7 | * Copyright © 2012-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include 14 | #include 15 | #include "lexeme.h" 16 | #include "support/logging.h" 17 | #include "support/utils.h" 18 | #include "support/strings.h" 19 | 20 | typedef enum { 21 | SCLASS_OWN, 22 | SCLASS_GLOBAL, 23 | SCLASS_PLIT, 24 | SCLASS_CODE, 25 | } storageclass_t; 26 | #define SCLASS_COUNT 4 27 | 28 | struct scopectx_s; 29 | typedef struct scopectx_s *scopectx_t; 30 | struct namectx_s; 31 | typedef struct namectx_s *namectx_t; 32 | struct name_s; 33 | typedef struct name_s name_t; 34 | 35 | // A 'nameref' is simply a pointer to a name table entry. 36 | // Sequences of such references are commonly used, so we 37 | // provide a basic type for the references themselves and 38 | // a tail queue they can be maintained in. 39 | struct nameref_s { 40 | TQ_ENT_FIELDS(struct nameref_s) 41 | struct name_s *np; 42 | }; 43 | typedef struct nameref_s nameref_t; 44 | 45 | struct namereflist_s { 46 | TQ_HDR_FIELDS(nameref_t) 47 | }; 48 | typedef struct namereflist_s namereflist_t; 49 | DEFINE_TQ_FUNCS(namereflist, namereflist_t, nameref_t) 50 | 51 | 52 | // Definitions for name table entries themselves. Name tables 53 | // are used to store and quickly search for identifiers of any 54 | // kind -- keywords, function names, symbol names, etc. -- so 55 | // an extension mechanism is provided for modules to hook into 56 | // to store additional information with the name. 57 | 58 | // The LRM limits names to 31 characters (NAME_SIZE-1). 59 | #define NAME_SIZE 32 60 | 61 | #define NAME_M_RESERVED (1<<0) // error if the name is redefined 62 | #define NAME_M_DECLARED (1<<1) // set if explicitly declared 63 | #define NAME_M_FORWARD (1<<2) 64 | #define NAME_M_BUILTIN (1<<3) 65 | #define NAME_M_FROMLIB (1<<4) 66 | #define NAME_M_FLAGMASK (0xFFFF) // others are reserved for internal use 67 | 68 | // To add more information to a name table entry, a module must register, 69 | // providing the size of the extension and, optionally, pointers to 70 | // constructor, destructor, and copy-constructor functions. The nametable 71 | // module will automatically manage the extension cell itself, but if the 72 | // extension in turn points to other allocated memory, that must be managed 73 | // by the registering module. 74 | typedef int (*name_datainit_fn)(void *ctx, name_t *np, void *p); 75 | typedef void (*name_datafree_fn)(void *ctx, name_t *np, void *p); 76 | typedef int (*name_datacopy_fn)(void *ctx, name_t *dst, void *dp, name_t *src, void *sp); 77 | // Function pointers for name types that get saved to, and read from, LIBARARY files. 78 | typedef int (*name_serialize_fn)(void *ctx, name_t *np, void *fh); 79 | typedef int (*name_deserialize_fn)(void *ctx, name_t *np, void *fh, unsigned int count); 80 | 81 | struct nametype_vectors_s { 82 | size_t typesize; 83 | name_datainit_fn typeinit; 84 | name_datafree_fn typefree; 85 | name_datacopy_fn typecopy; 86 | name_serialize_fn typeser; 87 | name_deserialize_fn typedes; 88 | }; 89 | typedef struct nametype_vectors_s nametype_vectors_t; 90 | 91 | struct namedef_s { 92 | lextype_t lt; 93 | unsigned int flags; 94 | size_t namelen; 95 | char *name; 96 | }; 97 | typedef struct namedef_s namedef_t; 98 | 99 | // Convenience macro for setting up static namedefs 100 | #define NAMEDEF(n_, lt_, f_) { (lt_), (f_), sizeof(n_)-1, (n_) } 101 | 102 | name_t *name_search(scopectx_t scope, const char *id, 103 | size_t len, lextype_t *ntypep); 104 | name_t *name_search_typed(scopectx_t scope, const char *id, 105 | size_t len, lextype_t ntype, void *datapp); 106 | name_t *name_search_typed_special(scopectx_t scope, const char *id, 107 | size_t len, lextype_t ntype, void *datapp); 108 | void name_insert(scopectx_t scope, name_t *name); 109 | void name_free(name_t *name); 110 | name_t *name_declare(scopectx_t scope, namedef_t *def, textpos_t pos, 111 | void *datap, size_t datasize, void *datapp); 112 | name_t *name_declare_nocheck(scopectx_t scope, namedef_t *def, textpos_t pos, 113 | void *datap, size_t datasize, void *datapp); 114 | lextype_t name_type(name_t *np); 115 | textpos_t name_defpos(name_t *np); 116 | void *name_extraspace(name_t *np); 117 | void *name_value_pointer(name_t *np); 118 | long name_value_signed(name_t *np); 119 | unsigned long name_value_unsigned(name_t *np); 120 | void name_value_pointer_set(name_t *np, void *ptr); 121 | void name_value_signed_set(name_t *np, long val); 122 | void name_value_unsigned_set(name_t *np, unsigned long val); 123 | int name_is_declared(scopectx_t scope, const char *id, size_t len); 124 | name_t *name_globalname(namectx_t namectx, name_t *np); 125 | scopectx_t name_scope(name_t *np); 126 | lexeme_t *name_to_lexeme(lexctx_t lctx, name_t *np); 127 | strdesc_t *name_string(name_t *np); 128 | char *name_azstring(name_t *np); 129 | unsigned int name_flags(name_t *np); 130 | namectx_t nametables_init(logctx_t logctx); 131 | void nametables_finish(namectx_t ctx); 132 | scopectx_t nametables_globalscope(namectx_t ctx); 133 | void *nametables_symctx_get(namectx_t ctx); 134 | void nametables_symctx_set(namectx_t ctx, void *p); 135 | scopectx_t scope_begin(namectx_t ctx, scopectx_t parent); 136 | scopectx_t scope_end(scopectx_t scope); 137 | scopectx_t scope_copy(scopectx_t src, scopectx_t newparent); 138 | scopectx_t scope_getparent(scopectx_t scope); 139 | namectx_t scope_namectx(scopectx_t scope); 140 | name_t *scope_sclass_psectname(scopectx_t scope, storageclass_t cl); 141 | void scope_sclass_psectname_set(scopectx_t scope, storageclass_t cl, name_t *np); 142 | void scope_setparent(scopectx_t scope, scopectx_t newparent); 143 | int scope_serialize(scopectx_t scope, void *fh, int allnames); 144 | int scope_deserialize(scopectx_t scope, void *fh, int allnames); 145 | void nametype_dataop_register(namectx_t ctx, lextype_t lt, 146 | nametype_vectors_t *vec, void *vctx); 147 | int name_undeclare(scopectx_t scope, name_t *np, textpos_t pos); 148 | size_t tempname_get(namectx_t ctx, char *buf, size_t bufsiz); 149 | 150 | nameref_t *nameref_alloc(namectx_t ctx, name_t *np); 151 | void nameref_free(namectx_t ctx, nameref_t *nr); 152 | void namereflist_free(namectx_t ctx, namereflist_t *reflist); 153 | int namereflist_copy(namectx_t ctx, namereflist_t *dst, 154 | namereflist_t *src); 155 | name_t *scope_nextname(scopectx_t scope, void **ctxp); 156 | int name_declare_builtin(scopectx_t scope, strdesc_t *str, textpos_t pos); 157 | int name_serialize(name_t *np, void *fh, void *extra, unsigned int extrasize); 158 | int name_deserialize(void *fh, char namebuf[NAME_SIZE], size_t *namelen, 159 | lextype_t *nametype, unsigned int *flags, unsigned int *count); 160 | int namereflist_serialize(namereflist_t *lst, void *fh); 161 | int namereflist_deserialize(scopectx_t scope, void *fh, namereflist_t *lst, unsigned int count); 162 | 163 | #endif /* nametable_h__ */ 164 | -------------------------------------------------------------------------------- /tests/runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # runtests 3 | # 4 | # Runs BLISS compiler tests. 5 | # 6 | # Tests are BLISS source files with comments embedded that 7 | # indicate the expected results. 8 | # 9 | # N.B.: requires Python 3. 10 | # 11 | # Copyright (c) 2013-2020, Matthew Madison 12 | # All rights reserved. 13 | # Distributed under license; see LICENSE.TXT for details. 14 | 15 | import sys 16 | import os 17 | import argparse 18 | import re 19 | import subprocess 20 | 21 | tests = [] 22 | skipped = 0 23 | tried = 0 24 | passed = 0 25 | failed = 0 26 | 27 | 28 | def process_test(testfile, blissc, cc, testharness, quiet=False, prefix='!!'): 29 | if not quiet: 30 | print("Processing: {}".format(testfile)) 31 | compout = [] 32 | comperrs = [] 33 | runout = [] 34 | failures = 0 35 | cases = 0 36 | with open(testfile, 'r') as infile: 37 | caselist = [line.rstrip() for line in infile.readlines() if line.startswith(prefix)] 38 | for case in caselist: 39 | s = case[len(prefix):].strip() 40 | cmd, junk, result = s.partition(' ') 41 | if cmd == 'cout': 42 | compout.append(result.strip()) 43 | elif cmd == 'cerr': 44 | comperrs.append(result.strip()) 45 | else: 46 | try: 47 | caseno = int(cmd) 48 | except ValueError: 49 | caseno = 0 50 | if caseno != 0: 51 | cases = cases + 1 52 | runout.append('TEST {:03d}: {}'.format(caseno, result.strip())) 53 | 54 | barename = os.path.splitext(os.path.basename(testfile))[0] 55 | try: 56 | docompile = subprocess.run([blissc, '-o', barename + '.o', '-I', '.', testfile], 57 | capture_output=True, encoding='utf-8', check=True) 58 | o = docompile.stdout.split('\n') 59 | if docompile.stderr: 60 | e = [line for line in docompile.stderr.split('\n') if line.startswith('%')] 61 | else: 62 | e = [] 63 | except subprocess.CalledProcessError as err: 64 | if err.returncode > 0 and len(comperrs) > 0: 65 | o = err.stdout.split('\n') 66 | e = [line for line in err.stderr.split('\n') if line.startswith('%')] 67 | pass 68 | else: 69 | print((" FAIL: compilation failed ({}) for {}" + 70 | "\n {}").format(err.returncode, testfile, 71 | '\n '.join([line for line in err.stderr.split('\n')]))) 72 | return cases, 0, cases 73 | 74 | for s in compout: 75 | if s in o: 76 | o.remove(s) 77 | else: 78 | failures = failures + 1 79 | if not quiet: 80 | print(" FAIL: compiler output [{}] not found".format(s)) 81 | if len(e) != 0 and len(comperrs) == 0: 82 | failures = failures + 1 83 | if not quiet: 84 | print(" FAIL: unexpected compilation errors:") 85 | for errline in e: 86 | print(" {}".format(errline)) 87 | else: 88 | for s in comperrs: 89 | if len(e) == 0: 90 | failures = failures + 1 91 | if not quiet: 92 | print(" FAIL: expected compilation error(s) not found") 93 | break 94 | cur = e[0] 95 | e = e[1:] 96 | if not cur.startswith(s): 97 | failures = failures + 1 98 | if not quiet: 99 | print(" FAIL: unmatched error: {}".format(s)) 100 | print(" actual: {}".format(cur)) 101 | if not quiet and failures == 0: 102 | print(" compilation phase passed") 103 | 104 | try: 105 | subprocess.run([cc, '-o', barename, testharness, barename + '.o'], check=True) 106 | except subprocess.CalledProcessError as e: 107 | print((" FAIL: linking {} into test harness failed" + 108 | "\n {}").format(barename + '.o', '\n '.join([line for line in e.stderr.split('\n')]))) 109 | return cases, 0, cases 110 | try: 111 | dorun = subprocess.run(['./' + barename], capture_output=True, encoding='utf-8', check=True) 112 | except subprocess.CalledProcessError as e: 113 | print((" FAIL: test harness failed to run for {}" + 114 | "\n {}").format(testfile, '\n '.join([line for line in e.stderr.split('\n')]))) 115 | return cases, 0, cases 116 | 117 | o = dorun.stdout.split('\n') 118 | passes = 0 119 | for s in runout: 120 | if s in o: 121 | o.remove(s) 122 | passes = passes + 1 123 | else: 124 | failures = failures + 1 125 | pfx = s.partition(':') 126 | actual = "" 127 | for outline in o: 128 | if outline.startswith(pfx): 129 | actual = outline 130 | break 131 | if actual == "": 132 | actual = "" 133 | else: 134 | o.remove(actual) 135 | if not quiet: 136 | print(" FAIL: wanted: {}".format(s)) 137 | print(" actual: {}".format(actual)) 138 | 139 | if not quiet: 140 | print(" {:d} of {:d} test cases passed".format(passes, cases)) 141 | return cases, passes, failures 142 | 143 | 144 | # begin main 145 | 146 | mydir, myname = os.path.split(sys.argv[0]) 147 | parser = argparse.ArgumentParser(description='Run blissc unit tests', prog=myname) 148 | parser.add_argument('testorsuite', help='test file name or test suite directory', nargs='*', default='.') 149 | parser.add_argument('--blissc', help='location of BLISS compiler', default='blissc') 150 | parser.add_argument('--harness', help='test harness C file', default=os.path.join(mydir, 'testharness.c')) 151 | parser.add_argument('--cc', help="C compiler", default=os.getenv("CC") or "cc") 152 | parser.add_argument('--quiet', '-q', action='store_true') 153 | 154 | args = parser.parse_args() 155 | 156 | pat = re.compile(r'^.+\.bli$') 157 | for t_or_s in args.testorsuite: 158 | if os.path.exists(t_or_s): 159 | if os.path.isdir(t_or_s): 160 | for d, sdl, fl in os.walk(t_or_s): 161 | for f in fl: 162 | if pat.match(f): 163 | tests.append(os.path.join(d, f)) 164 | else: 165 | tests.append(t_or_s) 166 | else: 167 | if not args.quiet: 168 | print("Skipping non-existent test: {}".format(t_or_s)) 169 | skipped = skipped + 1 170 | 171 | totcases = 0 172 | totpasses = 0 173 | for t in tests: 174 | tried = tried + 1 175 | c, p, f = process_test(t, args.blissc, args.cc, args.harness, args.quiet) 176 | totcases = totcases + c 177 | totpasses = totpasses + p 178 | if f == 0: 179 | passed = passed + 1 180 | else: 181 | failed = failed + 1 182 | 183 | if not args.quiet: 184 | print("-- Tried: {:d}; Skipped: {:d}; Passed: {:d}; Failed: {:d} --".format(tried, skipped, passed, failed)) 185 | print("-- Total test cases: {:d} ({:d} passing) --".format(totcases, totpasses)) 186 | if tried == 0 or failed != 0: 187 | sys.exit(1) 188 | sys.exit(0) 189 | -------------------------------------------------------------------------------- /include/blissc/symbols.h: -------------------------------------------------------------------------------- 1 | #ifndef symbols_h__ 2 | #define symbols_h__ 3 | /* 4 | *++ 5 | * symbols.h - Definitions for symbol names 6 | * 7 | * Copyright © 2012-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include "expression.h" 13 | #include "nametable.h" 14 | #include "lexeme.h" 15 | 16 | typedef enum { 17 | SYMSCOPE_LOCAL, 18 | SYMSCOPE_GLOBAL, 19 | SYMSCOPE_EXTERNAL 20 | } symscope_t; 21 | 22 | typedef enum { 23 | DCLASS_UNKNOWN, // for forwards 24 | DCLASS_STATIC, 25 | DCLASS_REGISTER, 26 | DCLASS_STKORREG, 27 | DCLASS_STACKONLY, 28 | DCLASS_ARG // subject to linkage 29 | } dataclass_t; 30 | 31 | #define SYM_M_VOLATILE (1U<<0U) 32 | #define SYM_M_ALIAS (1U<<1U) 33 | #define SYM_M_NOVALUE (1U<<2U) 34 | #define SYM_M_REF (1U<<3U) 35 | #define SYM_M_SIGNEXT (1U<<4U) 36 | #define SYM_M_RESERVED (1U<<5U) 37 | #define SYM_M_PENDING (1U<<6U) 38 | #define SYM_M_FORWARD (1U<<7U) 39 | #define SYM_M_BIND (1U<<8U) 40 | #define SYM_M_PLIT (1U<<9U) 41 | 42 | #define PSECT_M_ATTR_WRITE (1U<<13U) 43 | #define PSECT_M_ATTR_EXEC (1U<<14U) 44 | #define PSECT_M_ATTR_OVERLAY (1U<<15U) 45 | 46 | struct symctx_s; 47 | typedef struct symctx_s *symctx_t; 48 | 49 | struct literal_attr_s { 50 | unsigned long value; 51 | unsigned int width; 52 | unsigned int flags; 53 | symscope_t sc; 54 | }; 55 | typedef struct literal_attr_s literal_attr_t; 56 | 57 | struct data_attr_s { 58 | dataclass_t dclass; 59 | unsigned int units; 60 | name_t *struc; 61 | scopectx_t struscope; 62 | name_t *owner; 63 | initval_t *ivlist; 64 | namereflist_t fields; 65 | unsigned int flags; 66 | unsigned int alignment; 67 | unsigned int width; 68 | symscope_t sc; 69 | }; 70 | typedef struct data_attr_s data_attr_t; 71 | 72 | struct routine_attr_s { 73 | namereflist_t inargs; 74 | namereflist_t outargs; 75 | scopectx_t argscope; 76 | name_t *owner; 77 | unsigned int flags; 78 | symscope_t sc; 79 | initval_t *ivlist; 80 | }; 81 | typedef struct routine_attr_s routine_attr_t; 82 | 83 | // Initval structure - for handling compile-time 84 | // initialization of PLITs and data segments (via INITIAL 85 | // and PRESET). 86 | typedef enum { 87 | IVTYPE_SCALAR, 88 | IVTYPE_EXPR_EXP, 89 | IVTYPE_STRING, 90 | IVTYPE_LIST } ivtype_t; 91 | 92 | struct initval_s { 93 | struct initval_s *next; 94 | struct initval_s *lastptr; 95 | ivtype_t type; 96 | unsigned int repcount; 97 | void *preset_expr; 98 | union { 99 | struct { 100 | void *expr; 101 | long value; 102 | unsigned int width; 103 | int signext; 104 | } scalar; 105 | strdesc_t *string; 106 | struct initval_s *listptr; 107 | } data; 108 | }; 109 | 110 | typedef unsigned int (*sym_gensize_fn)(void *ctx, lextype_t lt); 111 | typedef int (*sym_geninit_fn)(void *ctx, name_t *np, void *p); 112 | typedef int (*sym_generator_fn)(void *ctx, name_t *np, void *p); 113 | typedef void (*sym_genfree_fn)(void *ctx, name_t *np, void *p); 114 | typedef int (*sym_gencopy_fn)(void *ctx, name_t *dnp, void *dp, 115 | name_t *snp, void *sp); 116 | 117 | struct sym_genvec_s { 118 | sym_gensize_fn sizefn; 119 | sym_generator_fn genfn; 120 | sym_geninit_fn geninit; 121 | sym_genfree_fn genfree; 122 | sym_gencopy_fn gencopy; 123 | 124 | }; 125 | typedef struct sym_genvec_s sym_genvec_t; 126 | 127 | symctx_t symbols_init(expr_ctx_t ctx); 128 | void symbols_connect_hooks(symctx_t ctx); 129 | void symbols_finish(symctx_t ctx); 130 | void symbols_gen_register(symctx_t ctx, void *genctx, sym_genvec_t *vec); 131 | name_t *datasym_search(scopectx_t scope, strdesc_t *namedsc, 132 | data_attr_t *attrp); 133 | name_t *datasym_declare(scopectx_t scope, strdesc_t *dsc, 134 | data_attr_t *attr, textpos_t pos); 135 | int datasym_attr_update(name_t *np, data_attr_t *attr); 136 | data_attr_t *datasym_attr(name_t *np); 137 | name_t *compiletime_declare(scopectx_t scope, strdesc_t *dsc, 138 | long val, textpos_t pos); 139 | void compiletime_assign(name_t *np, long val); 140 | long compiletime_value(name_t *np); 141 | name_t *label_declare(scopectx_t scope, strdesc_t *dsc, textpos_t pos); 142 | expr_node_t *label_block(name_t *np); 143 | void label_block_set(name_t *np, expr_node_t *b); 144 | name_t *litsym_search(scopectx_t scope, strdesc_t *dsc, unsigned long *valp); 145 | name_t *litsym_declare(scopectx_t scope, strdesc_t *dsc, 146 | literal_attr_t *attr, textpos_t pos); 147 | literal_attr_t *litsym_attr(name_t *np); 148 | name_t *litsym_special(scopectx_t scope, strdesc_t *dsc, 149 | unsigned long value); 150 | name_t *rtnsym_search(scopectx_t scope, strdesc_t *dsc); 151 | name_t *rtnsym_declare(scopectx_t scope, strdesc_t *dsc, 152 | routine_attr_t *attr, textpos_t pos); 153 | int rtnsym_attr_update(name_t *np, routine_attr_t *attr); 154 | expr_node_t *rtnsym_expr(name_t *np); 155 | void rtnsym_expr_set(name_t *np, expr_node_t *exp); 156 | routine_attr_t *rtnsym_attr(name_t *np); 157 | 158 | name_t *modsym_declare(scopectx_t scope, strdesc_t *str, textpos_t pos); 159 | strdesc_t *modsym_ident(name_t *np); 160 | void modsym_ident_set(name_t *np, strdesc_t *str); 161 | strdesc_t *modsym_main(name_t *np); 162 | void modsym_main_set(name_t *np, strdesc_t *str); 163 | expr_node_t *modsym_block(name_t *np); 164 | void modsym_block_set(name_t *np, expr_node_t *blk); 165 | int sym_undeclare(scopectx_t scope, strdesc_t *dsc, textpos_t pos); 166 | int sym_addrs_comparable(name_t *np_a, name_t *np_b); 167 | void sym_check_dangling_forwards(scopectx_t scope, textpos_t pos); 168 | void *sym_genspace(name_t *np); 169 | name_t *psect_declare(scopectx_t scope, strdesc_t *dsc, 170 | unsigned int psflags, textpos_t pos); 171 | name_t *psect_search(scopectx_t scope, strdesc_t *namedsc); 172 | unsigned int psect_attr(name_t *np); 173 | 174 | initval_t *initval_scalar_add(symctx_t ctx, initval_t *head, unsigned int reps, 175 | long val, unsigned int width, int signext); 176 | initval_t *initval_expr_add(symctx_t ctx, initval_t *head, unsigned int reps, 177 | void *exp, unsigned int width, int signext); 178 | initval_t *initval_scalar_prepend(symctx_t ctx, initval_t *head, unsigned int reps, 179 | long val, unsigned int width, int signext); 180 | initval_t *initval_string_add(symctx_t ctx, initval_t *head, unsigned int reps, 181 | strdesc_t *str); 182 | initval_t *initval_ivlist_add(symctx_t ctx, initval_t *head, unsigned int reps, 183 | initval_t *sublist); 184 | void initval_freelist(symctx_t ctx, initval_t *iv); 185 | unsigned int initval_size(symctx_t ctx, initval_t *ivlist); 186 | initval_t *preset_expr_add(symctx_t ctx, initval_t *head, void *pexp, void *exp); 187 | 188 | #endif /* symbols_h__ */ 189 | -------------------------------------------------------------------------------- /lib/support/logging.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * logging.c - Logging/error handling facility. 4 | * 5 | * This module implements logging of diagnostic messages, 6 | * based on the status code definitions (see statcodes.c/h). 7 | * When a fatal error is encountered, or the number of 8 | * non-fatal errors exceeds the maximum-error threshold, 9 | * compilation is aborted via longjmp(). Otherwise, diagnostics 10 | * are simply formatted and logged to stderr with no 11 | * interruption in flow. 12 | * 13 | * Text position will be reported with any diagnostic message, 14 | * if possible; for this to work, a function pointer must be 15 | * provided for this module to call to map the filename index 16 | * in the textpos_t type to an actual file name. 17 | * 18 | * Copyright © 2012-2020, Matthew Madison. 19 | * All rights reserved. 20 | * Distributed under license. See LICENSE.TXT for details. 21 | *-- 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include "blissc/support/logging.h" 27 | 28 | struct logctx_s { 29 | jmp_buf retenv; 30 | filename_fetch_fn fetchfn; 31 | line_fetch_fn linefetchfn; 32 | lstg_print_fn listprintfn; 33 | void *ffctx; 34 | void *lfctx; 35 | void *lpctx; 36 | unsigned int maxerrs; 37 | unsigned int infocount; 38 | unsigned int warncount; 39 | unsigned int errcount; 40 | int logtoterm; 41 | int logsrctolst; 42 | }; 43 | 44 | /* 45 | * logging_init 46 | * 47 | * Module initialization. The caller is expected to provide 48 | * the jmp_buf for us to longjmp() to when we need to abort. 49 | */ 50 | logctx_t 51 | logging_init (jmp_buf retenv) 52 | { 53 | logctx_t ctx = malloc(sizeof(struct logctx_s)); 54 | 55 | if (ctx != 0) { 56 | memset(ctx, 0, sizeof(struct logctx_s)); 57 | memcpy(ctx->retenv, retenv, sizeof(jmp_buf)); 58 | ctx->maxerrs = LOG_MAXERRS_DEFAULT; 59 | ctx->logtoterm = 1; 60 | } 61 | return ctx; 62 | 63 | } /* logging init */ 64 | 65 | /* 66 | * logging_finish 67 | * 68 | * Frees up the logging context. 69 | */ 70 | void 71 | logging_finish (logctx_t ctx) 72 | { 73 | free(ctx); 74 | 75 | } /* logging_finish */ 76 | 77 | /* 78 | * Getters/setters for logging context 79 | */ 80 | unsigned int log_maxerrs(logctx_t ctx) { return ctx->maxerrs; } 81 | void log_maxerrs_set(logctx_t ctx, unsigned int maxerrs) { ctx->maxerrs = maxerrs; } 82 | unsigned int log_infcount(logctx_t ctx) { return ctx->infocount; } 83 | unsigned int log_warncount(logctx_t ctx) { return ctx->warncount; } 84 | unsigned int log_errcount(logctx_t ctx) { return ctx->errcount; } 85 | void log_fetchfn_set(logctx_t ctx, filename_fetch_fn ffn, void *ffctx) { 86 | ctx->fetchfn = ffn; ctx->ffctx = ffctx; } 87 | void log_linefetchfn_set(logctx_t ctx, line_fetch_fn lfn, void *lfctx) { 88 | ctx->linefetchfn = lfn; ctx->lfctx = lfctx; } 89 | void log_lstgprintfn_set(logctx_t ctx, lstg_print_fn lpfn, void *lpctx) { 90 | ctx->listprintfn = lpfn; ctx->lpctx = lpctx; } 91 | void log_logterm_set (logctx_t ctx, int val) { ctx->logtoterm = val; } 92 | void log_logsrctolst_set (logctx_t ctx, int val) { ctx->logsrctolst = val; } 93 | 94 | /* 95 | * Print to both listing file and terminal 96 | */ 97 | static void 98 | doprint (logctx_t ctx, const char *buf, size_t buflen, int align_with_source) 99 | { 100 | if (ctx->listprintfn != 0) { 101 | (*ctx->listprintfn)(ctx->lpctx, buf, buflen, align_with_source); 102 | } 103 | if (ctx->logtoterm) { 104 | fprintf(stderr, "%-*.*s\n", (int) buflen, (int) buflen, buf); 105 | } 106 | } 107 | static void 108 | doprintsrc (logctx_t ctx, const char *buf, size_t buflen) 109 | { 110 | if (ctx->logsrctolst && ctx->listprintfn != 0) { 111 | (*ctx->listprintfn)(ctx->lpctx, buf, buflen, 0); 112 | } 113 | if (ctx->logtoterm) { 114 | fprintf(stderr, "%-*.*s\n", (int) buflen, (int) buflen, buf); 115 | } 116 | } 117 | 118 | /* 119 | * log_message 120 | * 121 | * Issue a message to the terminal (for %MESSAGE) 122 | */ 123 | void 124 | log_message (logctx_t ctx, const char *buf, size_t buflen) 125 | { 126 | fprintf(stderr, "%% %-*.*s\n", (int) buflen, (int) buflen, buf); 127 | 128 | } /* log_message */ 129 | 130 | /* 131 | * log_vsignal 132 | * 133 | * Core logging function. Formats the diagnostic message and text 134 | * position (if possible), emits the message on stderr, and handles 135 | * the next step - incrementing the appropriate diagnostic counter 136 | * and possibly aborting. 137 | */ 138 | void 139 | log_vsignal (logctx_t ctx, textpos_t pos, statcode_t code, va_list ap) 140 | { 141 | char logbuf[256]; 142 | int len; 143 | unsigned int sev = stc_severity(code); 144 | 145 | if (sev == STC_K_FATAL) { 146 | ctx->logtoterm = 1; 147 | } 148 | 149 | if (ctx->listprintfn != 0 || ctx->logtoterm) { 150 | int fileno = textpos_fileno(pos); 151 | unsigned int lineno = textpos_lineno(pos); 152 | unsigned int colno = textpos_colnum(pos); 153 | if (ctx->linefetchfn != 0) { 154 | strdesc_t *curline = (*ctx->linefetchfn)(ctx->lfctx, fileno, lineno); 155 | if (curline != 0) { 156 | doprintsrc(ctx, curline->ptr, curline->len); 157 | if (curline->len < sizeof(logbuf) && colno < curline->len) { 158 | memset(logbuf, '.', colno); 159 | logbuf[colno] = '|'; 160 | doprint(ctx, logbuf, colno+1, !ctx->logsrctolst); 161 | } 162 | } 163 | } 164 | len = stc_msg_vformat(code, logbuf, sizeof(logbuf)-1, ap); 165 | doprint(ctx, logbuf, (size_t) len, 0); 166 | if (pos != 0 && ctx->fetchfn != 0) { 167 | strdesc_t *fname = ctx->fetchfn(ctx->ffctx, fileno); 168 | if (fname != 0) { 169 | len = snprintf(logbuf, sizeof(logbuf)-1, "- at %-*.*s:%u:%u", 170 | (int) fname->len, (int) fname->len, fname->ptr, lineno, colno+1); 171 | if (len > 0) doprint(ctx, logbuf, (size_t) len, 0); 172 | } 173 | } 174 | } 175 | switch (sev) { 176 | case STC_K_ERROR: 177 | ctx->errcount += 1; 178 | if (ctx->errcount < ctx->maxerrs) break; 179 | // Force this error message to the terminal 180 | ctx->logtoterm = 1; 181 | len = snprintf(logbuf, sizeof(logbuf)-1, "%%BLISS-F-TOOMANYERRS, " 182 | "maximum number of errors exceeded, aborting"); 183 | if (len > 0) doprint(ctx, logbuf, (size_t) len, 0); 184 | // FALLTHROUGH 185 | case STC_K_FATAL: 186 | fflush(stderr); 187 | longjmp(ctx->retenv, 1); 188 | break; 189 | case STC_K_WARN: 190 | ctx->warncount += 1; 191 | break; 192 | case STC_K_INFO: 193 | ctx->infocount += 1; 194 | break; 195 | default: 196 | break; 197 | } 198 | 199 | } /* log_vsignal */ 200 | 201 | /* 202 | * log_signal 203 | * 204 | * The varargs API for logging. 205 | */ 206 | void 207 | log_signal (logctx_t ctx, textpos_t pos, statcode_t code, ...) 208 | { 209 | va_list ap; 210 | va_start(ap, code); 211 | log_vsignal(ctx, pos, code, ap); 212 | va_end(ap); 213 | 214 | } /* log_signal */ 215 | -------------------------------------------------------------------------------- /include/blissc/support/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef utils_h__ 2 | #define utils_h__ 3 | /* 4 | *++ 5 | * utils.h - Miscellaneous utility functions. 6 | * 7 | * Copyright © 2013-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include 13 | #include 14 | 15 | /* 16 | * Compiler info - version and compiling host. 17 | */ 18 | struct compilerinfo_s { 19 | unsigned int ver_major, ver_minor; 20 | const char *host_triple; 21 | }; 22 | typedef struct compilerinfo_s compilerinfo_t; 23 | 24 | 25 | /* 26 | * Text position tracking. A textpos is a 64-bit integer, divided 27 | * up into a file index number (the indices are tracked by the lexer), 28 | * a line number, and a column number. Every lexeme scanned carries 29 | * its text position; positions get saved with declarations. 30 | * 31 | * NB: If this is ported to a 32-bit host system, some adjustments 32 | * may need to be made. 33 | */ 34 | typedef uint64_t textpos_t; 35 | 36 | #define TEXTPOS_V_COLNUM 0 37 | #define TEXTPOS_S_COLNUM 16 38 | #define TEXTPOS_V_LINENO (TEXTPOS_V_COLNUM+TEXTPOS_S_COLNUM) 39 | #define TEXTPOS_S_LINENO 32 40 | #define TEXTPOS_V_FILENO (TEXTPOS_V_LINENO+TEXTPOS_S_LINENO) 41 | #define TEXTPOS_S_FILENO 16 42 | #define TEXTPOS_V_UNUSED (TEXTPOS_V_FILENO+TEXTPOS_S_FILENO) 43 | #define TEXTPOS_S_UNUSED (sizeof(textpos_t)*8-TEXTPOS_V_UNUSED) 44 | 45 | static inline __attribute__((unused)) textpos_t textpos_create(int fno, unsigned int lno, 46 | unsigned int cno) { 47 | return (textpos_t) ( 48 | (((uint64_t) fno & ~(~0ULL<> TEXTPOS_V_FILENO) & ~(~0ULL<> TEXTPOS_V_LINENO) & ~(~0ULL<> TEXTPOS_V_COLNUM) & ~(~0ULL<' or typedef'ed type) 111 | * for the queue header and queue entry structures, respectively. 112 | * 113 | * Functions (

represents the prefix mentioned above): 114 | *

_init: initializes the tail-queue header fields. 115 | *

_empty: returns 1 if the queue is empty. 116 | *

_inshead: insert an entry at the head of the queue. 117 | *

_instail: insert an entry at the tail of the queue. 118 | *

_append: append one queue to another. 119 | *

_prepend: prepend one queue at the front of another. 120 | *

_remhead: remove the entry at the head of the queue. 121 | *

_remtail: remove the entry at the tail of the queue. 122 | *

_remove: remove an arbitrary entry from the queue. 123 | *

_head: return a pointer to the first element in the queue. 124 | *

_tail: return a pointer to the last element in the queue. 125 | *

_length: return the number of entries in the queue. 126 | * 127 | * Note that the 'remove' function is the most expensive, requiring 128 | * a linear search of the queue until the entry is found. If you need 129 | * to do arbitrary removes frequently, a different queue implementation 130 | * would probably be in order. 131 | * 132 | */ 133 | #define TQ_HDR_FIELDS(enttype_) \ 134 | enttype_ *tq_head; \ 135 | enttype_ *tq_tail; \ 136 | unsigned int tq_count; 137 | #define TQ_ENT_FIELDS(enttype_) \ 138 | enttype_ *tq_next; 139 | #define DEFINE_TQ_FUNCS(pfx_, hdrtype_, enttype_) \ 140 | static inline __attribute__((unused)) void pfx_##_init (hdrtype_ *h) { \ 141 | h->tq_head = h->tq_tail = 0; h->tq_count = 0; } \ 142 | static inline __attribute__((unused)) int pfx_##_empty (hdrtype_ *h) { return (h->tq_count == 0); } \ 143 | static inline __attribute__((unused)) void pfx_##_inshead (hdrtype_ *h, enttype_ *e) { \ 144 | if (h->tq_count == 0) h->tq_tail = e; \ 145 | e->tq_next = h->tq_head; h->tq_head = e; h->tq_count += 1; } \ 146 | static inline __attribute__((unused)) void pfx_##_instail (hdrtype_ *h, enttype_ *e) { \ 147 | if (h->tq_count == 0) pfx_##_inshead(h, e); \ 148 | else { h->tq_tail->tq_next = e; e->tq_next = 0; h->tq_tail = e; h->tq_count += 1;}} \ 149 | static inline __attribute__((unused)) void pfx_##_append (hdrtype_ *dst, hdrtype_ *addon) { \ 150 | if (addon->tq_count == 0) return; \ 151 | if (dst->tq_count == 0) { \ 152 | dst->tq_head = addon->tq_head; dst->tq_tail = addon->tq_tail; \ 153 | dst->tq_count = addon->tq_count; } \ 154 | else { dst->tq_tail->tq_next = addon->tq_head; dst->tq_tail = addon->tq_tail; \ 155 | dst->tq_count += addon->tq_count; } \ 156 | addon->tq_head = addon->tq_tail = 0; addon->tq_count = 0; } \ 157 | static inline __attribute__((unused)) void pfx_##_prepend (hdrtype_ *dst, hdrtype_ *addon) { \ 158 | if (addon->tq_count == 0) return; \ 159 | if (dst->tq_count == 0) { \ 160 | dst->tq_head = addon->tq_head; dst->tq_tail = addon->tq_tail; \ 161 | dst->tq_count = addon->tq_count;} else { \ 162 | addon->tq_tail->tq_next = dst->tq_head; dst->tq_head = addon->tq_head; \ 163 | dst->tq_count += addon->tq_count; } \ 164 | addon->tq_head = addon->tq_tail = 0; addon->tq_count = 0; } \ 165 | static inline __attribute__((unused)) enttype_ * pfx_##_remhead (hdrtype_ *h) { \ 166 | enttype_ *e = h->tq_head; if (e == 0) return e; \ 167 | h->tq_head = e->tq_next; e->tq_next = 0; h->tq_count -= 1; return e; } \ 168 | static inline __attribute__((unused)) enttype_ * pfx_##_remove (hdrtype_ *h, enttype_ *e) { \ 169 | enttype_ *p = h->tq_head; if (p == 0) return 0; \ 170 | if (h->tq_count == 1) { if (p != e) return 0; \ 171 | h->tq_head = h->tq_tail = 0; h->tq_count = 0; return e; } \ 172 | while (p->tq_next != e) { p = p->tq_next; } \ 173 | p->tq_next = e->tq_next; h->tq_count -= 1; \ 174 | if (h->tq_tail == e) h->tq_tail = p; \ 175 | return e; } \ 176 | static inline __attribute__((unused)) enttype_ * pfx_##_remtail (hdrtype_ *h) { \ 177 | return pfx_##_remove(h, h->tq_tail); } \ 178 | static inline __attribute__((unused)) enttype_ * pfx_##_head (hdrtype_ *h) { return h->tq_head; } \ 179 | static inline __attribute__((unused)) enttype_ * pfx_##_tail (hdrtype_ *h) { return h->tq_tail; } \ 180 | static inline __attribute__((unused)) unsigned int pfx_##_length (hdrtype_ *h) { return h->tq_count; } 181 | 182 | #endif /* utils_h__ */ 183 | -------------------------------------------------------------------------------- /lib/frontend/switches.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * switches.c - Management of compiler switches 4 | * 5 | * This module implements the framework for handling 6 | * compiler switches and the parse routine for parsing 7 | * the contents of a SWITCHES declaration and the switches 8 | * that are specified on a MODULE declaration. 9 | * 10 | * There are two basic types of switches: toggle (called 'on-off' in 11 | * the LRM), and special. Toggle switches have two entries in the 12 | * name table: the base name of the toggle, and the "NO" form; 13 | * the lextype is used to differentiate the two. As an added bonus, 14 | * this module supports a more generic "value" toggle that has 15 | * the same mechanics, for use by other modules (cf. the listings 16 | * module). 17 | * 18 | * Special switches can take values in a variety of formats, and 19 | * so require a handler to parse the switch value and take the 20 | * appropriate action. Each special switch is a separate keyword 21 | * with its own lextype. 22 | * 23 | * Copyright © 2012-2020, Matthew Madison. 24 | * All rights reserved. 25 | * Distributed under license. See LICENSE.TXT for details. 26 | *-- 27 | */ 28 | #include "blissc/switches.h" 29 | 30 | struct switch_special_s { 31 | switch_parse_handler_fn handler; 32 | void *ctx; 33 | }; 34 | 35 | struct toggle_s { 36 | toggle_handler_fn handler; 37 | void *ctx; 38 | int togindex; 39 | }; 40 | 41 | static namedef_t special_switches[LEXTYPE_SWITCH_MAX-LEXTYPE_SWITCH_MIN+1] = { 42 | NAMEDEF("IDENT", LEXTYPE_SWITCH_IDENT, 0), 43 | NAMEDEF("LANGUAGE", LEXTYPE_SWITCH_LANGUAGE, 0), 44 | NAMEDEF("LIST", LEXTYPE_SWITCH_LIST, 0), 45 | NAMEDEF("MAIN", LEXTYPE_SWITCH_MAIN, 0), 46 | NAMEDEF("OPTLEVEL", LEXTYPE_SWITCH_OPTLEVEL, 0), 47 | NAMEDEF("VERSION", LEXTYPE_SWITCH_VERSION, 0), 48 | NAMEDEF("ADDRESSING_MODE", LEXTYPE_SWITCH_ADDRMODE, 0), 49 | NAMEDEF("ENVIRONMENT", LEXTYPE_SWITCH_ENV, 0), 50 | NAMEDEF("ENTRY", LEXTYPE_SWITCH_ENTRY, 0), 51 | NAMEDEF("OTS", LEXTYPE_SWITCH_OTS, 0), 52 | NAMEDEF("OTS_LINKAGE", LEXTYPE_SWITCH_OTSLNKG, 0) 53 | }; 54 | 55 | static strdesc_t linkage_kwd = STRDEF("LINKAGE"); 56 | static strdesc_t structure_kwd = STRDEF("STRUCTURE"); 57 | 58 | static lextype_t switch_types[] = { 59 | LEXTYPE_NAME_SW_TOGGLE_ON, 60 | LEXTYPE_NAME_SW_TOGGLE_OFF, 61 | LEXTYPE_DCL_STRUCTURE, 62 | LEXTYPE_DCL_LINKAGE, 63 | LEXTYPE_SWITCH_IDENT, 64 | LEXTYPE_SWITCH_LANGUAGE, 65 | LEXTYPE_SWITCH_LIST, 66 | LEXTYPE_SWITCH_MAIN, 67 | LEXTYPE_SWITCH_OPTLEVEL, 68 | LEXTYPE_SWITCH_VERSION, 69 | LEXTYPE_SWITCH_ADDRMODE, 70 | LEXTYPE_SWITCH_ENV, 71 | LEXTYPE_SWITCH_ENTRY, 72 | LEXTYPE_SWITCH_OTS, 73 | LEXTYPE_SWITCH_OTSLNKG 74 | }; 75 | 76 | /* 77 | * switch_special_declare 78 | * 79 | * Declares one of the "special" switch names and provides 80 | * a handler for it. 81 | */ 82 | int 83 | switch_special_declare (scopectx_t scope, lextype_t swtype, 84 | switch_parse_handler_fn handler, void *ctx) 85 | { 86 | name_t *np; 87 | struct switch_special_s *data; 88 | namedef_t *ndef; 89 | 90 | if (swtype != LEXTYPE_DCL_LINKAGE && swtype != LEXTYPE_DCL_STRUCTURE && 91 | (swtype < LEXTYPE_SWITCH_MIN || swtype > LEXTYPE_SWITCH_MAX)) { 92 | return 0; 93 | } 94 | if (swtype == LEXTYPE_DCL_LINKAGE) { 95 | np = name_search(scope, linkage_kwd.ptr, linkage_kwd.len, 0); 96 | } else if (swtype == LEXTYPE_DCL_STRUCTURE) { 97 | np = name_search(scope, structure_kwd.ptr, structure_kwd.len, 0); 98 | } else { 99 | ndef = &special_switches[swtype-LEXTYPE_SWITCH_MIN]; 100 | np = name_declare(scope, ndef, 0, 0, 0, 0); 101 | } 102 | 103 | if (np == 0) { 104 | return 0; 105 | } 106 | 107 | data = name_extraspace(np); 108 | data->handler = handler; 109 | data->ctx = ctx; 110 | return 1; 111 | 112 | } /* switch_special_declare */ 113 | 114 | /* 115 | * toggle_declare 116 | * 117 | * Declares ON and OFF toggle names for switches or values. 118 | * Some toggle names may conflict with other keywords; the caller 119 | * is expected to know these and to pass null pointers in for 120 | * the ON or OFF name_t pointers, as needed. 121 | */ 122 | static int 123 | toggle_declare (scopectx_t scope, int is_switch, strdesc_t *name, 124 | toggle_handler_fn handler, void *ctx, int togidx, 125 | name_t **onpp, name_t **offpp) 126 | { 127 | name_t *onp, *offp; 128 | struct toggle_s *datap; 129 | char namebuf[NAME_SIZE]; 130 | namedef_t ndef; 131 | 132 | memset(&ndef, 0, sizeof(ndef)); 133 | ndef.lt = (is_switch ? LEXTYPE_NAME_SW_TOGGLE_OFF : LEXTYPE_NAME_TOGGLE_OFF); 134 | ndef.name = namebuf; 135 | ndef.namelen = (name->len > NAME_SIZE-3) ? NAME_SIZE-1 : name->len+2; 136 | namebuf[0] = 'N'; namebuf[1] = 'O'; 137 | memcpy(namebuf+2, name->ptr, ndef.namelen-2); 138 | if (offpp != 0) { 139 | offp = name_declare(scope, &ndef, 0, 0, 0, &datap); 140 | if (offp == 0) { 141 | return 0; 142 | } 143 | datap->handler = handler; 144 | datap->ctx = ctx; 145 | datap->togindex = togidx; 146 | *offpp = offp; 147 | } else offp = 0; 148 | 149 | ndef.lt = (is_switch ? LEXTYPE_NAME_SW_TOGGLE_ON : LEXTYPE_NAME_TOGGLE_ON); 150 | ndef.name = namebuf + 2; 151 | ndef.namelen -= 2; 152 | if (onpp != 0) { 153 | onp = name_declare(scope, &ndef, 0, 0, 0, &datap); 154 | if (onp == 0) { 155 | if (offp != 0) name_free(offp); 156 | return 0; 157 | } 158 | datap->handler = handler; 159 | datap->ctx = ctx; 160 | datap->togindex = togidx; 161 | *onpp = onp; 162 | } 163 | return 1; 164 | 165 | } /* toggle_declare */ 166 | 167 | int switch_toggle_declare (scopectx_t scope, strdesc_t *name, toggle_handler_fn handler, 168 | void *ctx, int togidx, name_t **onpp, name_t **offpp) { 169 | return toggle_declare(scope, 1, name, handler, ctx, togidx, onpp, offpp); 170 | } 171 | int value_toggle_declare (scopectx_t scope, strdesc_t *name, toggle_handler_fn handler, 172 | void *ctx, int togidx, name_t **onpp, name_t **offpp) { 173 | return toggle_declare(scope, 0, name, handler, ctx, togidx, onpp, offpp); 174 | } 175 | 176 | /* 177 | * value_toggle_dispatch 178 | * 179 | * Dispatch the handler for a value toggle. 180 | */ 181 | int 182 | value_toggle_dispatch (parse_ctx_t pctx, void *ctx, lextype_t dcltype, name_t *togname) 183 | { 184 | struct toggle_s *t = name_extraspace(togname); 185 | 186 | if (name_type(togname) != LEXTYPE_NAME_TOGGLE_OFF && 187 | name_type(togname) != LEXTYPE_NAME_TOGGLE_ON) { 188 | return 0; 189 | } 190 | 191 | return (*t->handler)(pctx, ctx, t->togindex, dcltype, togname); 192 | 193 | } /* value_toggle_dispatch */ 194 | 195 | /* 196 | * parse_switches 197 | * 198 | * Parses SWITCHES declarations and MODULE switches. 199 | * 200 | */ 201 | int 202 | parse_switches (parse_ctx_t pctx, lextype_t decltype, lextype_t terminator) 203 | { 204 | int i; 205 | lexeme_t *lex; 206 | name_t *np; 207 | 208 | while (1) { 209 | i = parser_expect_oneof(pctx, QL_NORMAL, 210 | switch_types, sizeof(switch_types)/sizeof(switch_types[0]), 211 | &lex, 1); 212 | if (i < 0) { 213 | log_signal(parser_logctx(pctx), parser_curpos(pctx), STC__SWITCHEXP); 214 | break; 215 | } 216 | np = lexeme_ctx_get(lex); 217 | if (np == 0) { 218 | log_signal(parser_logctx(pctx), parser_curpos(pctx), STC__SWITCHUNS, 219 | lexeme_text(lex)); 220 | } else { 221 | lextype_t lt; 222 | lt = lexeme_type(lex); 223 | if (lt == LEXTYPE_NAME_SW_TOGGLE_OFF || lt == LEXTYPE_NAME_SW_TOGGLE_ON) { 224 | struct toggle_s *t = name_extraspace(np); 225 | if (t->handler == 0) { 226 | log_signal(parser_logctx(pctx), parser_curpos(pctx), 227 | STC__SWITCHUNS, lexeme_text(lex)); 228 | } else { 229 | (*t->handler)(pctx, t->ctx, t->togindex, decltype, np); 230 | } 231 | } else { 232 | struct switch_special_s *s = name_extraspace(np); 233 | if (s->handler == 0) { 234 | log_signal(parser_logctx(pctx), parser_curpos(pctx), 235 | STC__SWITCHUNS, lexeme_text(lex)); 236 | } else { 237 | (*s->handler)(pctx, s->ctx, decltype, lex); 238 | } 239 | } 240 | } 241 | lexeme_free(parser_lexmemctx(pctx), lex); 242 | if (parser_expect(pctx, QL_NORMAL, terminator, 0, 1)) { 243 | return 1; 244 | } 245 | if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_DELIM_COMMA, 0, 1)) { 246 | log_signal(parser_logctx(pctx), parser_curpos(pctx), STC__DELIMEXP, ","); 247 | } 248 | 249 | } /* while 1 */ 250 | 251 | // If we broke out of the loop, there was some kind of error, try to recover 252 | parser_skip_to_delim(pctx, terminator); 253 | return 1; 254 | 255 | } /* parse_switches */ 256 | -------------------------------------------------------------------------------- /lib/llvmgen/llvmgen.h: -------------------------------------------------------------------------------- 1 | #ifndef llvmgen_h__ 2 | #define llvmgen_h__ 3 | /* 4 | *++ 5 | * llvmgen.h - Common definitions for LLVM code generation. 6 | * 7 | * Copyright © 2013-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include "blissc/gencode.h" 14 | #include "blissc/execfuncs.h" 15 | #include "blissc/expression.h" 16 | #include "blissc/symbols.h" 17 | #include "blissc/machinedef.h" 18 | #include "llvm_machinectx.h" 19 | #include 20 | #include 21 | 22 | #define LLVMGEN_K_PHIREFMAX 256 23 | #define LLVMGEN_K_MAXARGS 16 24 | 25 | // Branch tracking structure. There are common patterns 26 | // for handling LLVM branches and phi values for loops, 27 | // conditionals, etc., encapsulated in the llvmgen_btrack_XXX 28 | // functions (some of which are inlined below). 29 | // 30 | // There are three types of expressions for which these 31 | // structures need to be exposed globally -- routines, 32 | // blocks, and loops. The structure is also stackable, since 33 | // we can have nested expressions of each of these types. 34 | struct llvm_btrack_s { 35 | struct llvm_btrack_s *next; 36 | LLVMBasicBlockRef exitblock; 37 | LLVMBasicBlockRef phisources[LLVMGEN_K_PHIREFMAX]; 38 | LLVMValueRef phivals[LLVMGEN_K_PHIREFMAX]; 39 | unsigned int branchcount; 40 | unsigned int phirefcount; 41 | }; 42 | typedef struct llvm_btrack_s llvm_btrack_t; 43 | 44 | 45 | // Routine tracking structure. Holds the information needed 46 | // globally while generating code for a routine. Stackable in 47 | // the gencode-context structure, since routine definitions 48 | // can be nested. 49 | 50 | #define LLVMGEN_K_BT_FUNC 0 51 | #define LLVMGEN_K_BT_BLK 1 52 | #define LLVMGEN_K_BT_LOOP 2 53 | #define LLVMGEN_K_BTCOUNT 3 54 | 55 | struct llvm_rtntrack_s { 56 | struct llvm_rtntrack_s *next; 57 | llvm_btrack_t *btrack[LLVMGEN_K_BTCOUNT]; 58 | LLVMBuilderRef builder; 59 | LLVMValueRef func; 60 | LLVMBasicBlockRef bldpos; 61 | unsigned int tmpidx; 62 | unsigned int lblidx; 63 | }; 64 | typedef struct llvm_rtntrack_s llvm_rtntrack_t; 65 | 66 | // Assembly instruction definition. 67 | // XXX Should this be made private? 68 | #define LLVMGEN_K_ASM_MAXARGS 8 69 | #define LLVMGEN_M_ASM_SIDEEFFECT (1<<0) 70 | #define LLVMGEN_M_ASM_ALIGNSTACK (1<<1) 71 | struct llvm_asminstr_s { 72 | void *instrinfo; 73 | LLVMTypeRef functype; 74 | LLVMTypeRef rettype; 75 | LLVMTypeRef argtypes[LLVMGEN_K_ASM_MAXARGS]; 76 | LLVMValueRef xargs[LLVMGEN_K_ASM_MAXARGS]; 77 | LLVMValueRef asminstr; 78 | unsigned int flags; 79 | unsigned int argcount; 80 | }; 81 | typedef struct llvm_asminstr_s llvm_asminstr_t; 82 | 83 | // Function pointers for the dispatchers 84 | typedef LLVMValueRef (*llvmgen_expgen_fn)(gencodectx_t, expr_node_t *, LLVMTypeRef); 85 | typedef LLVMValueRef (*llvmgen_execfunc_fn)(gencodectx_t, void *, expr_node_t *, LLVMTypeRef); 86 | 87 | // Executable function structure. Used in multiple modules. 88 | struct llvm_execfuncgen_s { 89 | const char * name; 90 | llvmgen_execfunc_fn func; 91 | void *fctx; 92 | }; 93 | typedef struct llvm_execfuncgen_s llvm_execfuncgen_t; 94 | 95 | // Global code-generation context. 96 | struct gencodectx_s { 97 | machine_ctx_t mctx; 98 | expr_ctx_t ectx; 99 | symctx_t symctx; 100 | machinedef_t *mach; 101 | name_t *modnp; 102 | llvm_btrack_t *freebts; 103 | llvm_rtntrack_t *freerts; 104 | llvm_rtntrack_t *curfn; 105 | void *builtinsctx; 106 | scopectx_t asmscope; 107 | llvmgen_expgen_fn expgen_funcs[EXPTYPE_COUNT]; 108 | 109 | LLVMContextRef llvmctx; 110 | LLVMModuleRef module; 111 | 112 | LLVMTypeRef unittype; 113 | LLVMTypeRef unitptrtype; 114 | LLVMTypeRef fullwordtype; 115 | LLVMTypeRef int1type; 116 | LLVMTypeRef intptrtszype; 117 | 118 | unsigned int globidx; 119 | unsigned int optlevel; 120 | 121 | char tmpnambuf[NAME_SIZE]; 122 | }; 123 | 124 | 125 | // LLVM storage class for data segments. 126 | typedef enum { 127 | LLVM_REG, 128 | LLVM_LOCAL, 129 | LLVM_GLOBAL 130 | } llvm_stgclass_t; 131 | 132 | // Access information structure, used to pass information 133 | // about a data segment being accessed (for a fetch or an 134 | // assignment). 135 | #define LLVMGEN_M_SEG_SIGNEXT (1U<<0U) 136 | #define LLVMGEN_M_SEG_VOLATILE (1U<<1U) 137 | #define LLVMGEN_M_SEG_DEREFED (1U<<2U) 138 | #define LLVMGEN_M_SEG_ISREF (1U<<3U) 139 | #define LLVMGEN_M_SEG_ISBIND (1U<<4U) 140 | #define LLVMGEN_M_SEG_BINDPTR (1U<<7U) 141 | // Flags above are also used in the accinfo structure below, 142 | // along with the following flags 143 | #define LLVMGEN_M_ACC_CONSTSIZ (1U<<5U) 144 | #define LLVMGEN_M_ACC_GENEXPR (1U<<6U) 145 | struct llvm_accinfo_s { 146 | llvm_stgclass_t segclass; 147 | unsigned int flags; 148 | unsigned int size; 149 | unsigned int width; 150 | LLVMValueRef posval, sizeval; 151 | }; 152 | typedef struct llvm_accinfo_s llvm_accinfo_t; 153 | 154 | // Commonly-used functions. XXX Revisit inlining decisions 155 | #define siu static inline __attribute__((unused)) 156 | siu char *llvmgen_temp(gencodectx_t gctx) { 157 | snprintf(gctx->tmpnambuf, sizeof(gctx->tmpnambuf), "tmp.%u", gctx->curfn->tmpidx++); 158 | return gctx->tmpnambuf; 159 | } 160 | siu char *llvmgen_label(gencodectx_t gctx) { 161 | snprintf(gctx->tmpnambuf, sizeof(gctx->tmpnambuf), "label.%u", gctx->curfn->lblidx++); 162 | return gctx->tmpnambuf; 163 | } 164 | siu char *llvmgen_global(gencodectx_t gctx) { 165 | snprintf(gctx->tmpnambuf, sizeof(gctx->tmpnambuf), "$$global$$.%u", gctx->globidx++); 166 | return gctx->tmpnambuf; 167 | } 168 | siu void llvmgen_btrack_update_brcount(gencodectx_t gctx, llvm_btrack_t *bt) { 169 | bt->branchcount += 1; 170 | } 171 | siu void llvmgen_btrack_update_phi(gencodectx_t gctx, llvm_btrack_t *bt, 172 | LLVMBasicBlockRef pos, LLVMValueRef val) { 173 | if (pos == 0) pos = LLVMGetInsertBlock(gctx->curfn->builder); 174 | bt->phisources[bt->phirefcount] = pos; 175 | bt->phivals[bt->phirefcount] = val; 176 | bt->phirefcount += 1; 177 | } 178 | siu void llvmgen_btrack_update(gencodectx_t gctx, llvm_btrack_t *bt, LLVMValueRef val) { 179 | 180 | LLVMBasicBlockRef here = LLVMGetInsertBlock(gctx->curfn->builder); 181 | if (LLVMGetBasicBlockTerminator(here) == 0) { 182 | bt->branchcount += 1; 183 | LLVMBuildBr(gctx->curfn->builder, bt->exitblock); 184 | if (val != 0) llvmgen_btrack_update_phi(gctx, bt, here, val); 185 | } 186 | } 187 | 188 | LLVMValueRef llvmgen_cast_trunc_ext(gencodectx_t gctx, LLVMValueRef val, 189 | LLVMTypeRef neededtype, int signext); 190 | 191 | siu LLVMValueRef llvmgen_adjustval(gencodectx_t gctx, LLVMValueRef val, 192 | LLVMTypeRef neededtype, int signext) { 193 | if (val == 0 || neededtype == 0 || LLVMTypeOf(val) == neededtype) return val; 194 | return llvmgen_cast_trunc_ext(gctx, val, neededtype, signext); 195 | } 196 | #undef siu 197 | 198 | LLVMBasicBlockRef llvmgen_exitblock_create(gencodectx_t gctx, char *label); 199 | llvm_btrack_t *llvmgen_btrack_create(gencodectx_t gctx, LLVMBasicBlockRef exitpoint); 200 | 201 | LLVMValueRef llvmgen_btrack_finalize(gencodectx_t gctx, llvm_btrack_t *bt, LLVMTypeRef neededtype); 202 | void llvmgen_symgen_init(gencodectx_t gctx); 203 | llvm_btrack_t *llvmgen_label_btrack(name_t *np); 204 | void llvmgen_label_btrack_set(name_t *np, llvm_btrack_t *bt); 205 | void llvmgen_memcpy(gencodectx_t gctx, LLVMValueRef dest, LLVMValueRef src, LLVMValueRef len); 206 | void llvmgen_memset(gencodectx_t gctx, LLVMValueRef dest, LLVMValueRef val, LLVMValueRef len); 207 | LLVMValueRef llvmgen_assignment(gencodectx_t gctx, expr_node_t *lhs, expr_node_t *rhs); 208 | LLVMValueRef llvmgen_segaddress(gencodectx_t gctx, name_t *np, llvm_stgclass_t *segclass, 209 | unsigned int *flagsp, LLVMTypeRef *llvm_type); 210 | LLVMValueRef llvmgen_expression(gencodectx_t gctx, expr_node_t *exp, LLVMTypeRef neededtype); 211 | LLVMValueRef llvmgen_addr_expression(gencodectx_t gctx, expr_node_t *exp, llvm_accinfo_t *accinfo); 212 | LLVMValueRef llvmgen_rtntype_info(gencodectx_t gctx, name_t *np, LLVMTypeRef *func_type, LLVMTypeRef *return_type); 213 | void llvmgen_deref_push(gencodectx_t gctx, name_t *np); 214 | void llvmgen_deref_pop(gencodectx_t gctx, name_t *np); 215 | void llvmgen_expgen_register(gencodectx_t gctx, exprtype_t type, llvmgen_expgen_fn func); 216 | void llvmgen_opexpgen_init(gencodectx_t gctx); 217 | void llvmgen_ctrlexpgen_init(gencodectx_t gctx); 218 | void llvmgen_execfuncgen_init(gencodectx_t gctx); 219 | void llvmgen_expgen_init(gencodectx_t gctx); 220 | int llvmgen_predfromop(optype_t op, int addrsigned, LLVMIntPredicate *predp); 221 | void *llvmgen_builtins_init(gencodectx_t gctx, scopectx_t kwdscope); 222 | void llvmgen_builtins_finish(void *actx); 223 | LLVMValueRef llvmgen_builtinfunc(gencodectx_t gctx, const char *name, 224 | expr_node_t *exp, LLVMTypeRef neededtype); 225 | LLVMValueRef llvmgen_asminstr(gencodectx_t gctx, const char *name, LLVMValueRef *args, 226 | unsigned int argcnt); 227 | #endif /* llvmgen_h__ */ 228 | -------------------------------------------------------------------------------- /lib/frontend/execfuncs.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * execfuncs.c - Executable functions. 4 | * 5 | * This module implements the dispatch mechanism for 6 | * handling executable function calls, as well as 7 | * implementing the standard functions. 8 | * 9 | * Copyright © 2012-2024, Matthew Madison. 10 | * All rights reserved. 11 | * Distributed under license. See LICENSE.TXT for details. 12 | *-- 13 | */ 14 | #include "blissc/execfuncs.h" 15 | #include "blissc/charfuncs.h" 16 | 17 | static int cmplss (long val1, long val2) { return (val1 < val2); } 18 | static int cmplssu (long val1, long val2) { 19 | return ((unsigned long) val1 < (unsigned long)val2); } 20 | static int cmpgtr (long val1, long val2) { return (val1 > val2); } 21 | static int cmpgtru (long val1, long val2) { 22 | return ((unsigned long) val1 > (unsigned long) val2); } 23 | 24 | static expr_node_t *execfunc_MINMAX(expr_ctx_t ctx, compare_fn fn, 25 | name_t *fnp, exprseq_t *arglist, textpos_t curpos); 26 | static expr_node_t *execfunc_SIGN(expr_ctx_t ctx, compare_fn fn, name_t *fnp, 27 | exprseq_t *arglist, textpos_t curpos); 28 | static expr_node_t *execfunc_ABS(expr_ctx_t ctx, compare_fn fn, name_t *fnp, 29 | exprseq_t *arglist, textpos_t curpos); 30 | 31 | static funcdef_t stdfuncs[] = { 32 | FUNCDEF("MAX", execfunc_MINMAX, cmpgtr, 2, FUNC_M_VARARGS), 33 | FUNCDEF("MAXA", execfunc_MINMAX, cmpgtr, 2, FUNC_M_VARARGS), 34 | FUNCDEF("MAXU", execfunc_MINMAX, cmpgtru, 2, FUNC_M_VARARGS), 35 | FUNCDEF("MIN", execfunc_MINMAX, cmplss, 2, FUNC_M_VARARGS), 36 | FUNCDEF("MINA", execfunc_MINMAX, cmplss, 2, FUNC_M_VARARGS), 37 | FUNCDEF("MINU", execfunc_MINMAX, cmplssu, 2, FUNC_M_VARARGS), 38 | FUNCDEF("SIGN", execfunc_SIGN, 0, 1, 0), 39 | FUNCDEF("ABS", execfunc_ABS, 0, 1, 0) 40 | }; 41 | 42 | 43 | /* 44 | * parse_func_args 45 | * 46 | * Utility routine for parsing argument lists for functions. 47 | */ 48 | static int 49 | parse_func_args (expr_ctx_t ctx, exprseq_t *arglist) 50 | { 51 | parse_ctx_t pctx = expr_parse_ctx(ctx); 52 | 53 | if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_DELIM_LPAR, 0, 1)) { 54 | return 0; 55 | } 56 | while (1) { 57 | expr_node_t *exp; 58 | if (!expr_parse_expr(ctx, &exp)) { 59 | expr_signal(ctx, STC__EXPREXP); 60 | exp = expr_node_alloc(ctx, EXPTYPE_PRIM_LIT, parser_curpos(pctx)); 61 | } 62 | exprseq_instail(arglist, exp); 63 | if (parser_expect(pctx, QL_NORMAL, LEXTYPE_DELIM_RPAR, 0, 1)) { 64 | break; 65 | } 66 | if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_DELIM_COMMA, 0, 1)) { 67 | expr_signal(ctx, STC__DELIMEXP, ","); 68 | } 69 | } 70 | 71 | return 1; 72 | 73 | } /* parse_func_args */ 74 | 75 | /* 76 | * function_bind 77 | * 78 | * expr dispatch function for executable functions 79 | * 80 | * Parses the argument list and either creates an expression 81 | * node for the function call or invokes the per-function 82 | * dispatcher for further processing. 83 | * 84 | */ 85 | static expr_node_t * 86 | function_bind (expr_ctx_t ctx, lextype_t lt, lexeme_t *lex) 87 | { 88 | name_t *np = lexeme_ctx_get(lex); 89 | textpos_t pos = parser_curpos(expr_parse_ctx(ctx)); 90 | funcdef_t *func; 91 | exprseq_t args; 92 | expr_node_t *result; 93 | 94 | if (np == 0) { 95 | expr_signal(ctx, STC__INTCMPERR, "function_bind"); 96 | return 0; 97 | } 98 | 99 | func = name_extraspace(np); 100 | exprseq_init(&args); 101 | if ((func->flags & FUNC_M_NOPARSE) == 0 && !parse_func_args(ctx, &args)) { 102 | expr_signal(ctx, STC__SYNTAXERR); 103 | return 0; 104 | } 105 | 106 | if (func->handler == 0) { 107 | if ((func->flags & FUNC_M_NOPARSE) == 0) { 108 | int varargs = (func->flags & FUNC_M_VARARGS) != 0; 109 | if ((varargs && exprseq_length(&args) < func->numargs) || 110 | (!varargs && exprseq_length(&args) != func->numargs)) { 111 | expr_signal(ctx, STC__INSFUNARG, name_string(np)); 112 | } 113 | } 114 | result = expr_node_alloc(ctx, EXPTYPE_EXECFUN, pos); 115 | if (result != 0) { 116 | expr_func_name_set(result, np); 117 | exprseq_append(expr_func_arglist(result), &args); 118 | expr_has_value_set(result, (func->flags & FUNC_M_NOVALUE) == 0); 119 | } 120 | } else { 121 | result = (*func->handler)(ctx, func->fn, np, &args, pos); 122 | } 123 | 124 | if (result == 0) { 125 | exprseq_free(ctx, &args); 126 | } 127 | 128 | return result; 129 | 130 | } /* function_bind */ 131 | 132 | /* 133 | * execfunc_define 134 | * 135 | * Entry point for other modules that need to declare 136 | * executable functions. 137 | */ 138 | name_t * 139 | execfunc_define (scopectx_t scope, funcdef_t *funcdef, textpos_t pos) 140 | { 141 | namedef_t ndef; 142 | memset(&ndef, 0, sizeof(ndef)); 143 | ndef.lt = LEXTYPE_NAME_FUNCTION; 144 | ndef.name = funcdef->name; 145 | ndef.namelen = funcdef->namelen; 146 | ndef.flags |= (funcdef->flags & FUNC_M_BUILTIN) == 0 ? 0 : NAME_M_BUILTIN; 147 | return name_declare(scope, &ndef, pos, funcdef, sizeof(funcdef_t), 0); 148 | 149 | } /* execfunc_define */ 150 | 151 | /* 152 | * execfunc_init 153 | * 154 | * Initialization for this module. Called by expr_init. 155 | */ 156 | void 157 | execfunc_init (expr_ctx_t ctx, scopectx_t scope) 158 | { 159 | unsigned int i; 160 | namedef_t ndef; 161 | nametype_vectors_t fvec; 162 | 163 | memset(&fvec, 0, sizeof(fvec)); 164 | fvec.typesize = sizeof(funcdef_t); 165 | nametype_dataop_register(expr_namectx(ctx), LEXTYPE_NAME_FUNCTION, &fvec, 0); 166 | expr_dispatch_register(ctx, LEXTYPE_NAME_FUNCTION, function_bind); 167 | 168 | memset(&ndef, 0, sizeof(ndef)); 169 | ndef.lt = LEXTYPE_NAME_FUNCTION; 170 | for (i = 0; i < sizeof(stdfuncs)/sizeof(stdfuncs[0]); i++) { 171 | ndef.name = stdfuncs[i].name; 172 | ndef.namelen = stdfuncs[i].namelen; 173 | name_declare(scope, &ndef, 0, &stdfuncs[i], sizeof(stdfuncs[i]), 0); 174 | } 175 | 176 | charfuncs_init(ctx, scope); 177 | 178 | } /* execfunc_init */ 179 | 180 | /* 181 | * execfunc_MINMAX 182 | * 183 | * Implements the MAX{A|U|} and MIN{A|U|} functions, automatically 184 | * reducing compile-time-constant arguments to the appropriate single 185 | * value. 186 | */ 187 | static expr_node_t * 188 | execfunc_MINMAX (expr_ctx_t ctx, compare_fn cmpfn, name_t *fnp, exprseq_t *arglist, 189 | textpos_t curpos) 190 | { 191 | machinedef_t *mach = expr_machinedef(ctx); 192 | strdesc_t *fname = name_string(fnp); 193 | exprseq_t newargs; 194 | expr_node_t *arg, *result; 195 | long curmax = -1; 196 | int did1 = 0; 197 | 198 | if (fname->len > 3 && fname->ptr[3] == 'A' && !machine_addr_signed(mach)) { 199 | cmpfn = (cmpfn == cmplss ? cmplssu : cmpgtru); 200 | } 201 | exprseq_init(&newargs); 202 | for (arg = exprseq_remhead(arglist); arg != 0; arg = exprseq_remhead(arglist)) { 203 | if (expr_type(arg) == EXPTYPE_PRIM_LIT) { 204 | long thisval = expr_litval(arg); 205 | if (did1) { 206 | if (cmpfn(thisval, curmax)) { 207 | curmax = thisval; 208 | } 209 | } else { 210 | curmax = thisval; 211 | did1 = 1; 212 | } 213 | expr_node_free(ctx, arg); 214 | } else { 215 | exprseq_instail(&newargs, arg); 216 | } 217 | } 218 | result = 0; 219 | if (did1) { 220 | arg = expr_node_alloc(ctx, EXPTYPE_PRIM_LIT, curpos); 221 | expr_litval_set(arg, curmax); 222 | expr_has_value_set(arg, 1); 223 | expr_is_ctce_set(arg, 1); 224 | if (exprseq_length(&newargs) == 0) { 225 | result = arg; 226 | } else { 227 | exprseq_instail(&newargs, arg); 228 | } 229 | } 230 | if (result == 0) { 231 | result = expr_node_alloc(ctx, EXPTYPE_EXECFUN, curpos); 232 | expr_func_name_set(result, fnp); 233 | exprseq_append(expr_func_arglist(result), &newargs); 234 | expr_has_value_set(result, 1); 235 | } 236 | 237 | return result; 238 | 239 | } /* func_MINMAX */ 240 | 241 | /* 242 | * execfunc_SIGN 243 | * 244 | * SIGN() function. 245 | */ 246 | static expr_node_t * 247 | execfunc_SIGN (expr_ctx_t ctx, compare_fn fn __attribute__((unused)), 248 | name_t *fnp, exprseq_t *arglist, textpos_t curpos) 249 | { 250 | expr_node_t *result = exprseq_head(arglist); 251 | 252 | if (expr_type(result) == EXPTYPE_PRIM_LIT) { 253 | long val = expr_litval(result); 254 | result = exprseq_remhead(arglist); 255 | expr_litval_set(result, (val == 0 ? 0 : (val > 0 ? 1L : -1L))); 256 | } else { 257 | result = expr_node_alloc(ctx, EXPTYPE_EXECFUN, curpos); 258 | expr_func_name_set(result, fnp); 259 | exprseq_append(expr_func_arglist(result), arglist); 260 | expr_has_value_set(result, 1); 261 | } 262 | 263 | return result; 264 | 265 | } /* execfunc_SIGN */ 266 | 267 | /* 268 | * execfunc_ABS 269 | * 270 | * ABS() function. 271 | */ 272 | static expr_node_t * 273 | execfunc_ABS (expr_ctx_t ctx, compare_fn fn __attribute__((unused)), name_t *fnp, 274 | exprseq_t *arglist, textpos_t curpos) 275 | { 276 | expr_node_t *result = exprseq_head(arglist); 277 | machinedef_t *mach = expr_machinedef(ctx); 278 | 279 | if (expr_type(result) == EXPTYPE_PRIM_LIT) { 280 | long val = expr_litval(result); 281 | result = exprseq_remhead(arglist); 282 | expr_litval_set(result, getvalue(labs(val), machine_scalar_bits(mach), 0)); 283 | } else { 284 | result = expr_node_alloc(ctx, EXPTYPE_EXECFUN, curpos); 285 | expr_func_name_set(result, fnp); 286 | exprseq_append(expr_func_arglist(result), arglist); 287 | expr_has_value_set(result, 1); 288 | } 289 | 290 | return result; 291 | 292 | } /* execfunc_ABS */ 293 | -------------------------------------------------------------------------------- /driver/blissc.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * blissc.c - Main driver program for blissc 4 | * 5 | * Provides a command line interface for invoking 6 | * the compiler. 7 | * 8 | * Copyright © 2013-2020, Matthew Madison. 9 | * All rights reserved. 10 | * Distributed under license. See LICENSE.TXT for details. 11 | *-- 12 | */ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "blissc/driver.h" 18 | 19 | enum { 20 | LONGOPT_SHOW = 4, 21 | LONGOPT_DUMP_IR = 6, 22 | LONGOPT_VERSION = 8, 23 | LONGOPT_LIBRARY = 9 24 | }; 25 | static struct option options[] = { 26 | { "output", required_argument, 0, 'o' }, 27 | { "assembly", no_argument, 0, 's' }, 28 | { "optimization", required_argument, 0, 'O' }, 29 | { "listing", optional_argument, 0, 'l' }, 30 | { "show", required_argument, 0, 0 }, 31 | { "variant", optional_argument, 0, 'V' }, 32 | { "dump-ir", optional_argument, 0, 0 }, 33 | { "help", no_argument, 0, 'h' }, 34 | { "version", no_argument, 0, 0 }, 35 | { "library", no_argument, 0, 0 }, 36 | { "include", required_argument, 0, 'I' }, 37 | { 0, 0, 0, 0 } 38 | }; 39 | static char *optarghelp[] = { 40 | "--output=filename ", 41 | "--assembly ", 42 | "--optimization={0,1,2,3}", 43 | "--listing[=filename] ", 44 | "--show[=option,...] ", 45 | "--variant= ", 46 | "--dump-ir[=filename] ", 47 | "--help ", 48 | "--version ", 49 | "--library ", 50 | "--include " 51 | }; 52 | static char *opthelp[] = { 53 | "output file name (default is src name +'.o' or '.s' suffix)", 54 | "generate assembly language output instead of object", 55 | "set optimization level (valid values: 0,1,2,3, default:1)", 56 | "generate listing (default name is output file with '.lis' suffix)", 57 | "controls listing output (see below)", 58 | "sets the %VARIANT value; if no value specified, defaults to 1", 59 | "dumps the LLVM IR code (default name is outfile + '.ll' suffix)", 60 | "displays usage information and exits", 61 | "displays version information and exits", 62 | "generate a LIBRARY file", 63 | "directory for locationg REQUIRE and LIBRARY files" 64 | }; 65 | static char *listopts [] = { 66 | "source","require","expand","trace", 67 | "library","object","assembly","binary", 68 | "commentary" 69 | }; 70 | static char *listopthelp[] = { 71 | "source listing (default: on)", 72 | "include REQUIRE files in listing (default: off)", 73 | "show macro expansions (default: off)", 74 | "trace macro expansions (default: off)", 75 | "(not supported yet)", 76 | "(not supported yet)", 77 | "include assembly in listing (not supported yet)", 78 | "(not supported yet)", 79 | "(not supported yet)" 80 | }; 81 | 82 | static char *srcfile = 0, *outfile = 0, *listfile = 0; 83 | static unsigned int listflags = 0; 84 | static bliss_output_t outtype = BLISS_K_OUTPUT_OBJECT; 85 | static int optlevel = -1; 86 | static unsigned int variant = 0; 87 | static int dumpir = 0; 88 | static char *irfile = 0; 89 | 90 | static void 91 | print_version (void) 92 | { 93 | printf("%s version %s\n", blissc_package_name(), 94 | blissc_package_version()); 95 | 96 | } /* print_version */ 97 | 98 | static void 99 | print_usage (void) 100 | { 101 | unsigned int i; 102 | print_version(); 103 | printf("\nUsage:\n"); 104 | printf("\tblissc [options] filename\n\n"); 105 | printf("Options:\n"); 106 | for (i = 0; i < sizeof(options)/sizeof(options[0]) && options[i].name != 0; i++) { 107 | printf(" %s\t%c%c\t%s\n", 108 | optarghelp[i], 109 | (options[i].val == 0 ? ' ' : '-'), 110 | (options[i].val == 0 ? ' ' : options[i].val), 111 | opthelp[i]); 112 | } 113 | printf("--show options:\n"); 114 | for (i = 0; i < sizeof(listopts)/sizeof(listopts[0]); i++) { 115 | printf(" %-16.16s %s\n", listopts[i], listopthelp[i]); 116 | } 117 | 118 | } /* print_usage */ 119 | 120 | static void 121 | parse_args (blissc_driverctx_t cctx, int argc, char *argv[]) 122 | { 123 | int c, which, err; 124 | char *showopts; 125 | 126 | err = 0; 127 | while (!err) { 128 | c = getopt_long_only(argc, argv, "hsl::o:O:V::I:", options, &which); 129 | if (c == -1) { 130 | break; 131 | } 132 | switch (c) { 133 | case 0: 134 | if (which == LONGOPT_SHOW) { 135 | if (optarg == 0) { 136 | fprintf(stderr, "error in options processing"); 137 | err = 1; 138 | break; 139 | } 140 | showopts = optarg; 141 | err = 0; 142 | while (*showopts != '\0') { 143 | char *value; 144 | int i = getsubopt(&showopts, listopts, &value); 145 | if (i < 0) { 146 | fprintf(stderr, "Unrecognized --show option: %s\n", value); 147 | err = 1; 148 | break; 149 | } 150 | listflags |= (1<= '0' && *optarg <= '3' && *(optarg+1) == '\0') { 199 | optlevel = *optarg - '0'; 200 | } else { 201 | fprintf(stderr, "Unrecognized optimization level: %s\n", optarg); 202 | err = 1; 203 | } 204 | break; 205 | case 'V': 206 | if (optarg == 0) { 207 | variant = 1; 208 | } else { 209 | char *cp; 210 | variant = (unsigned int)strtol(optarg, &cp, 10); 211 | } 212 | break; 213 | case '?': 214 | fprintf(stderr, "Unrecognized option: %s\n", argv[optind]); 215 | err = 1; 216 | break; 217 | case ':': 218 | fprintf(stderr, "Required argument missing\n"); 219 | err = 1; 220 | break; 221 | default: 222 | fprintf(stderr, "Unknown option processing error\n"); 223 | err = 1; 224 | break; 225 | } 226 | 227 | } /* while not err */ 228 | 229 | if (!err) { 230 | if (optind < argc) { 231 | srcfile = argv[optind]; 232 | } else { 233 | fprintf(stderr, "Missing required argument (source file name)\n"); 234 | err = 1; 235 | } 236 | } 237 | 238 | if (err) { 239 | exit((err == 999 ? 0 : err)); 240 | } 241 | 242 | } /* parse_args */ 243 | 244 | int 245 | main (int argc, char *argv[]) 246 | { 247 | jmp_buf retenv; 248 | blissc_driverctx_t cctx = 0; 249 | int status = 0; 250 | 251 | if (setjmp(retenv)) { 252 | status = 1; 253 | goto finish; 254 | } 255 | cctx = blissc_init(retenv); 256 | parse_args(cctx, argc, argv); 257 | 258 | if (!blissc_target_set(cctx, 0)) { 259 | fprintf(stderr, "error setting target\n"); 260 | status = 999; 261 | goto finish; 262 | } 263 | if (listflags != 0 || listfile != 0) { 264 | if (listflags == 0) listflags = BLISS_M_LIST_SRC; 265 | if (!blissc_listopt_set(cctx, listflags, listfile, -1)) { 266 | fprintf(stderr, "Error setting listing options\n"); 267 | status = 1; 268 | goto finish; 269 | } 270 | } 271 | if (!blissc_output_set(cctx, outtype, outfile, -1)) { 272 | fprintf(stderr, "Error setting output type\n"); 273 | status = 1; 274 | goto finish; 275 | } 276 | if (outtype != BLISS_K_OUTPUT_LIBRARY) { 277 | if (!blissc_dumpir_set(cctx, dumpir, irfile, -1)) { 278 | fprintf(stderr, "Error setting --dump-ir\n"); 279 | status = 1; 280 | goto finish; 281 | } 282 | if (optlevel >= 0) { 283 | if (!blissc_optlevel_set(cctx, (unsigned int) optlevel)) { 284 | fprintf(stderr, "Error setting optimization level"); 285 | status = 1; 286 | goto finish; 287 | } 288 | } 289 | } 290 | blissc_variant_set(cctx, variant); 291 | if (!blissc_compile(cctx, srcfile, -1)) { 292 | fprintf(stderr, "bliss_compile reported error\n"); 293 | status = 998; 294 | goto finish; 295 | } 296 | finish: 297 | blissc_finish(cctx); 298 | return status; 299 | 300 | } /* main */ 301 | -------------------------------------------------------------------------------- /include/blissc/support/statcodes.h: -------------------------------------------------------------------------------- 1 | #ifndef statcodes_h__ 2 | #define statcodes_h__ 3 | /* 4 | *++ 5 | * statcodes.h - status code and message definitions. 6 | * 7 | * Copyright © 2013-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | #include 13 | #include 14 | #include 15 | 16 | // The statcode_t type is represented as an enum in C, but 17 | // it's really a 32-bit integer of which 3 bits are used to 18 | // represent a severity code and 29 bits are used for a 19 | // message number. 20 | #define STC_V_SEVERITY 29 21 | #define STC_S_SEVERITY 3 22 | #define STC_V_MSGNO 0 23 | #define STC_S_MSGNO 29 24 | 25 | // Note the pattern here - the high-order bit is 26 | // set for severity levels that should be treated as 27 | // failures, making the OK/FAIL a simple sign check 28 | // on the status code. The remaining two bits are 29 | // used to differentiate the levels of OK-ness or 30 | // or failure. 31 | #define STC_K_SUCCESS 1U // 001 32 | #define STC_K_INFO 2U // 010 33 | #define STC_K_WARN 5U // 101 34 | #define STC_K_ERROR 6U // 110 35 | #define STC_K_FATAL 7U // 111 36 | 37 | // Macros to compose status codes by packing the fields into the 38 | // right bit positions. 39 | #define STC_MAKECODE(sev_, msgno_) \ 40 | (((sev_)<> STC_V_SEVERITY) & ~((~0U) << STC_S_SEVERITY); } 186 | static inline __attribute__((unused)) unsigned int stc_msgno (statcode_t s) { 187 | return (s >> STC_V_MSGNO) & ~((~0U) << STC_S_MSGNO); } 188 | static inline __attribute__((unused)) int stc_success (statcode_t s) { return (int) s >= 0; } 189 | static inline __attribute__((unused)) int stc_fail (statcode_t s) { return (int) s < 0; } 190 | 191 | int stc_msg_format(statcode_t statcode, char *buf, size_t bufsiz, ...); 192 | int stc_msg_vformat(statcode_t statcode, char *buf, size_t bufsiz, va_list ap); 193 | 194 | #endif /* statcodes_h__ */ 195 | -------------------------------------------------------------------------------- /include/blissc/lexeme.h: -------------------------------------------------------------------------------- 1 | #ifndef lexeme_h__ 2 | #define lexeme_h__ 3 | /* 4 | *++ 5 | * lexeme.h - Lexeme definitions. 6 | * 7 | * Copyright © 2012-2020, Matthew Madison. 8 | * All rights reserved. 9 | * Distributed under license. See LICENSE.TXT for details. 10 | *-- 11 | */ 12 | 13 | #include "support/logging.h" 14 | #include "support/fileio.h" 15 | #include "support/strings.h" 16 | #include "support/utils.h" 17 | 18 | /* 19 | * The following enums represent lexical processing 20 | * state, and are included here because the lexeme_bind() 21 | * routine lives in lexeme.c and needs this information. 22 | */ 23 | typedef enum { 24 | QL_NORMAL, QL_NAME, QL_MACRO, QL_MACROSKIP 25 | } quotelevel_t; 26 | 27 | typedef enum { 28 | QM_NONE, QM_QUOTE, QM_UNQUOTE, QM_EXPAND 29 | } quotemodifier_t; 30 | 31 | typedef enum { 32 | COND_NORMAL = 0, 33 | COND_CWC, COND_CWA, 34 | COND_AWC, COND_AWA 35 | } condstate_t; 36 | 37 | // Factory macros for lexeme types. 38 | // 39 | // NB: if adding a new type, make sure you update the 40 | // MIN and MAX definitions below when applicable. 41 | // 42 | // NB: Modules may depend on the specific placement and 43 | // ordering of these type codes (e.g., for array indexing), 44 | // so DO NOT change the order or placement of any existing 45 | // type code unless you know what you are doing! 46 | 47 | // Naming convention for identifying classes of lexeme types: 48 | // 49 | // xxx = basic lexeme type or special marker 50 | // OP_xxx = operator (single char or keyword) 51 | // DELIM_xxx = delimiter (single char only) 52 | // LXF_xxx = lexical function 53 | // NAME_xxx = Declarable names, builtins, and executable functions 54 | // DCL_xxx = declaration keyword 55 | // ATTR_xxx = attribute for a data or routine symbol 56 | // AU_xxx = allocation-unit 57 | // EXP_DELIM_xx = expression-level delimiter (BEGIN and END) 58 | // CTRL_xxx = control-expression introducer 59 | // KWD_xxx = other keyword, semantics may depend on context 60 | #undef DOLEXTYPE 61 | #define DOLEXTYPES \ 62 | DOLEXTYPE(NONE) \ 63 | DOLEXTYPE(END) \ 64 | DOLEXTYPE(MARKER) \ 65 | DOLEXTYPE(MACROEND) \ 66 | DOLEXTYPE(UNBOUND) \ 67 | DOLEXTYPE(NUMERIC) \ 68 | DOLEXTYPE(STRING) \ 69 | DOLEXTYPE(CSTRING) \ 70 | DOLEXTYPE(OP_ADD) DOLEXTYPE(OP_SUB) \ 71 | DOLEXTYPE(OP_MUL) DOLEXTYPE(OP_DIV) \ 72 | DOLEXTYPE(OP_MOD) \ 73 | DOLEXTYPE(OP_ASSIGN) DOLEXTYPE(OP_FETCH) \ 74 | DOLEXTYPE(OP_SHIFT) \ 75 | DOLEXTYPE(OP_AND) DOLEXTYPE(OP_EQV) DOLEXTYPE(OP_OR) \ 76 | DOLEXTYPE(OP_NOT) DOLEXTYPE(OP_XOR) \ 77 | DOLEXTYPE(OP_EQL) DOLEXTYPE(OP_NEQ) \ 78 | DOLEXTYPE(OP_LSS) DOLEXTYPE(OP_LEQ) \ 79 | DOLEXTYPE(OP_GTR) DOLEXTYPE(OP_GEQ) \ 80 | DOLEXTYPE(OP_EQLU) DOLEXTYPE(OP_NEQU) \ 81 | DOLEXTYPE(OP_LSSU) DOLEXTYPE(OP_LEQU) \ 82 | DOLEXTYPE(OP_GTRU) DOLEXTYPE(OP_GEQU) \ 83 | DOLEXTYPE(OP_EQLA) DOLEXTYPE(OP_NEQA) \ 84 | DOLEXTYPE(OP_LSSA) DOLEXTYPE(OP_LEQA) \ 85 | DOLEXTYPE(OP_GTRA) DOLEXTYPE(OP_GEQA) \ 86 | DOLEXTYPE(DELIM_COMMA) \ 87 | DOLEXTYPE(DELIM_SEMI) \ 88 | DOLEXTYPE(DELIM_COLON) \ 89 | DOLEXTYPE(DELIM_LPAR) \ 90 | DOLEXTYPE(DELIM_RPAR) \ 91 | DOLEXTYPE(DELIM_LBRACK) \ 92 | DOLEXTYPE(DELIM_RBRACK) \ 93 | DOLEXTYPE(DELIM_LANGLE) \ 94 | DOLEXTYPE(DELIM_RANGLE) \ 95 | DOLEXTYPE(LXF_ASCII) DOLEXTYPE(LXF_ASCIC) \ 96 | DOLEXTYPE(LXF_ASCIZ) DOLEXTYPE(LXF_B) DOLEXTYPE(LXF_O) \ 97 | DOLEXTYPE(LXF_DECIMAL) DOLEXTYPE(LXF_X) \ 98 | DOLEXTYPE(LXF_C) DOLEXTYPE(LXF_STRING) DOLEXTYPE(LXF_EXACTSTRING) \ 99 | DOLEXTYPE(LXF_CHARCOUNT) DOLEXTYPE(LXF_CHAR) DOLEXTYPE(LXF_EXPLODE) \ 100 | DOLEXTYPE(LXF_REMOVE) DOLEXTYPE(LXF_NAME) DOLEXTYPE(LXF_QUOTENAME) \ 101 | DOLEXTYPE(LXF_NULL) DOLEXTYPE(LXF_IDENTICAL) \ 102 | DOLEXTYPE(LXF_ISSTRING) DOLEXTYPE(LXF_ASSIGN) \ 103 | DOLEXTYPE(LXF_REQUIRE) DOLEXTYPE(LXF_QUOTE) DOLEXTYPE (LXF_UNQUOTE) \ 104 | DOLEXTYPE(LXF_EXPAND) DOLEXTYPE(LXF_IF) DOLEXTYPE(LXF_THEN) \ 105 | DOLEXTYPE(LXF_ELSE) DOLEXTYPE (LXF_FI) DOLEXTYPE(LXF_DELIM_PERCENT) \ 106 | DOLEXTYPE(LXF_CTCE) DOLEXTYPE(LXF_LTCE) DOLEXTYPE(LXF_VARIANT) \ 107 | DOLEXTYPE(LXF_NUMBER) DOLEXTYPE(LXF_NBITS) DOLEXTYPE(LXF_NBITSU) \ 108 | DOLEXTYPE(LXF_ALLOCATION) DOLEXTYPE(LXF_SIZE) \ 109 | DOLEXTYPE(LXF_FIELDEXPAND) DOLEXTYPE(LXF_DECLARED) \ 110 | DOLEXTYPE(LXF_SWITCHES) DOLEXTYPE(LXF_BLISS) \ 111 | DOLEXTYPE(LXF_ERROR) DOLEXTYPE(LXF_WARN) \ 112 | DOLEXTYPE(LXF_INFORM) DOLEXTYPE(LXF_PRINT) \ 113 | DOLEXTYPE(LXF_MESSAGE) DOLEXTYPE(LXF_TITLE) DOLEXTYPE(LXF_SBTTL) \ 114 | DOLEXTYPE(LXF_EXITMACRO) DOLEXTYPE(LXF_EXITITER) \ 115 | DOLEXTYPE(LXF_ERRORMACRO) DOLEXTYPE(LXF_REMAINING) \ 116 | DOLEXTYPE(LXF_COUNT) DOLEXTYPE(LXF_LENGTH) \ 117 | DOLEXTYPE(NAME) \ 118 | DOLEXTYPE(NAME_MACRO) DOLEXTYPE(NAME_MAC_PARAM) \ 119 | DOLEXTYPE(NAME_COMPILETIME) DOLEXTYPE(NAME_LITERAL) \ 120 | DOLEXTYPE(NAME_DATA) DOLEXTYPE(NAME_STRUCTURE) \ 121 | DOLEXTYPE(NAME_LINKAGE) DOLEXTYPE(NAME_ROUTINE) \ 122 | DOLEXTYPE(NAME_MODULE) DOLEXTYPE(NAME_FUNCTION) \ 123 | DOLEXTYPE(NAME_LABEL) DOLEXTYPE(NAME_FIELD) \ 124 | DOLEXTYPE(NAME_FIELDSET) \ 125 | DOLEXTYPE(NAME_SW_TOGGLE_ON) DOLEXTYPE(NAME_SW_TOGGLE_OFF) \ 126 | DOLEXTYPE(NAME_TOGGLE_ON) DOLEXTYPE(NAME_TOGGLE_OFF) \ 127 | DOLEXTYPE(NAME_LANG) DOLEXTYPE(NAME_ADDRMODE) \ 128 | DOLEXTYPE(NAME_ENVOPT) DOLEXTYPE(NAME_OTSOPT) \ 129 | DOLEXTYPE(NAME_CPU) DOLEXTYPE(NAME_MONITOR) \ 130 | DOLEXTYPE(NAME_PSECT) \ 131 | DOLEXTYPE(SWITCH_IDENT) DOLEXTYPE(SWITCH_LANGUAGE) \ 132 | DOLEXTYPE(SWITCH_LIST) \ 133 | DOLEXTYPE(SWITCH_MAIN) DOLEXTYPE(SWITCH_OPTLEVEL) \ 134 | DOLEXTYPE(SWITCH_VERSION) DOLEXTYPE(SWITCH_ADDRMODE) \ 135 | DOLEXTYPE(SWITCH_ENV) DOLEXTYPE(SWITCH_ENTRY) \ 136 | DOLEXTYPE(SWITCH_OTS) DOLEXTYPE(SWITCH_OTSLNKG) \ 137 | DOLEXTYPE(DCL_MACRO) DOLEXTYPE(DCL_KEYWORDMACRO) \ 138 | DOLEXTYPE(DCL_LITERAL) DOLEXTYPE(DCL_GLOBAL) DOLEXTYPE(DCL_FORWARD) \ 139 | DOLEXTYPE(DCL_EXTERNAL) DOLEXTYPE(DCL_OWN) DOLEXTYPE(DCL_LOCAL) \ 140 | DOLEXTYPE(DCL_STACKLOCAL) DOLEXTYPE(DCL_STRUCTURE) DOLEXTYPE(DCL_LABEL) \ 141 | DOLEXTYPE(DCL_MODULE) DOLEXTYPE(DCL_ELUDOM) DOLEXTYPE(DCL_LINKAGE) \ 142 | DOLEXTYPE(DCL_ENABLE) DOLEXTYPE(DCL_BIND) DOLEXTYPE(DCL_REQUIRE) \ 143 | DOLEXTYPE(DCL_LIBRARY) DOLEXTYPE(DCL_PSECT) DOLEXTYPE(DCL_SWITCHES) \ 144 | DOLEXTYPE(DCL_BUILTIN) DOLEXTYPE(DCL_ROUTINE) DOLEXTYPE(DCL_REGISTER) \ 145 | DOLEXTYPE(DCL_UNDECLARE) DOLEXTYPE(DCL_MAP) DOLEXTYPE(DCL_FIELD) \ 146 | DOLEXTYPE(DCL_COMPILETIME) \ 147 | DOLEXTYPE(ATTR_SIGNED) DOLEXTYPE(ATTR_UNSIGNED) \ 148 | DOLEXTYPE(ATTR_VOLATILE) DOLEXTYPE(ATTR_ALIGN) \ 149 | DOLEXTYPE(ATTR_ALIAS) DOLEXTYPE(ATTR_INITIAL) \ 150 | DOLEXTYPE(ATTR_PRESET) DOLEXTYPE(ATTR_NOVALUE) \ 151 | DOLEXTYPE(AU_BYTE) DOLEXTYPE(AU_WORD) \ 152 | DOLEXTYPE(AU_LONG) DOLEXTYPE(AU_QUAD) \ 153 | DOLEXTYPE(EXP_DELIM_BEGIN) DOLEXTYPE(EXP_DELIM_END) \ 154 | DOLEXTYPE(KWD_PLIT) DOLEXTYPE(KWD_UPLIT) DOLEXTYPE(KWD_CODECOMMENT) \ 155 | DOLEXTYPE(CTRL_IF) DOLEXTYPE(CTRL_THEN) DOLEXTYPE(CTRL_ELSE) \ 156 | DOLEXTYPE(CTRL_CASE) DOLEXTYPE(CTRL_SELECT) DOLEXTYPE(CTRL_SELECTONE) \ 157 | DOLEXTYPE(CTRL_SELECTU) DOLEXTYPE(CTRL_SELECTA) \ 158 | DOLEXTYPE(CTRL_SELECTONEU) DOLEXTYPE(CTRL_SELECTONEA) \ 159 | DOLEXTYPE(CTRL_INCR) DOLEXTYPE(CTRL_INCRA) DOLEXTYPE(CTRL_INCRU) \ 160 | DOLEXTYPE(CTRL_DECR) DOLEXTYPE(CTRL_DECRA) DOLEXTYPE(CTRL_DECRU) \ 161 | DOLEXTYPE(CTRL_EXITLOOP) DOLEXTYPE(CTRL_LEAVE) DOLEXTYPE(CTRL_RETURN) \ 162 | DOLEXTYPE(CTRL_WHILE) DOLEXTYPE(CTRL_UNTIL) DOLEXTYPE(CTRL_DO) \ 163 | DOLEXTYPE(KWD_FROM) DOLEXTYPE(KWD_TO) DOLEXTYPE(KWD_OF) \ 164 | DOLEXTYPE(KWD_BY) DOLEXTYPE(KWD_SET) DOLEXTYPE(KWD_TES) \ 165 | DOLEXTYPE(KWD_INRANGE) DOLEXTYPE(KWD_OUTRANGE) \ 166 | DOLEXTYPE(KWD_OTHERWISE) DOLEXTYPE(KWD_ALWAYS) DOLEXTYPE(KWD_REP) \ 167 | DOLEXTYPE(KWD_CODE) DOLEXTYPE(KWD_NODEFAULT) \ 168 | DOLEXTYPE(KWD_WRITE) DOLEXTYPE(KWD_NOWRITE) \ 169 | DOLEXTYPE(KWD_EXECUTE) DOLEXTYPE(KWD_NOEXECUTE) \ 170 | DOLEXTYPE(KWD_OVERLAY) DOLEXTYPE(KWD_CONCATENATE) DOLEXTYPE(KWD_REF) \ 171 | DOLEXTYPE(KWD_WITH) DOLEXTYPE(KWD_PCTREF) \ 172 | DOLEXTYPE(KWD_EXTERNAL) DOLEXTYPE(KWD_NONEXTERNAL) \ 173 | DOLEXTYPE(KWD_STACK) 174 | 175 | #define DOLEXTYPE(lt) LEXTYPE_##lt, 176 | typedef enum { 177 | DOLEXTYPES 178 | LEXTYPE_COUNT // MUST BE LAST 179 | } lextype_t; 180 | #undef DOLEXTYPE 181 | 182 | #define LEXTYPE_MIN LEXTYPE_END 183 | #define LEXTYPE_MAX (LEXTYPE_COUNT-1) 184 | 185 | #define LEXTYPE_OP_MIN LEXTYPE_OP_ADD 186 | #define LEXTYPE_OP_MAX LEXTYPE_OP_GEQA 187 | 188 | #define LEXTYPE_DELIM_MIN LEXTYPE_DELIM_COMMA 189 | #define LEXTYPE_DELIM_MAX LEXTYPE_DELIM_RANGLE 190 | 191 | // NB: This range is used by the parser module for 192 | // indexing into the lexical function dispatch array 193 | #define LEXTYPE_LXF_MIN LEXTYPE_LXF_ASCII 194 | #define LEXTYPE_LXF_MAX LEXTYPE_LXF_LENGTH 195 | 196 | // NB: This range is used by the nametable module 197 | // for indexing into the name type handling array 198 | #define LEXTYPE_NAME_MIN LEXTYPE_NAME 199 | #define LEXTYPE_NAME_MAX LEXTYPE_NAME_PSECT 200 | 201 | #define LEXTYPE_SWITCH_MIN LEXTYPE_SWITCH_IDENT 202 | #define LEXTYPE_SWITCH_MAX LEXTYPE_SWITCH_OTSLNKG 203 | 204 | // NB: This range is used by the expression module 205 | // for indexing into the expression-level dispatch array 206 | #define LEXTYPE_EXPKWD_MIN LEXTYPE_DCL_MACRO 207 | #define LEXTYPE_EXPKWD_MAX LEXTYPE_MAX 208 | 209 | #define LEXTYPE_DCL_MIN LEXTYPE_DCL_MACRO 210 | #define LEXTYPE_DCL_MAX LEXTYPE_DCL_COMPILETIME 211 | 212 | #define LEXTYPE_ATTR_MIN LEXTYPE_ATTR_SIGNED 213 | #define LEXTYPE_ATTR_MAX LEXTYPE_ATTR_NOVALUE 214 | 215 | #define LEXTYPE_AU_MIN LEXTYPE_AU_BYTE 216 | #define LEXTYPE_AU_MAX LEXTYPE_AU_QUAD 217 | 218 | #define LEX_M_ALLOCATED (1<<0) 219 | 220 | // The internals of the lexeme structure are exposed here, 221 | // since it is used so frequently throughout the various 222 | // modules. DO NOT directly reference the fields, however; 223 | // use the getter/setter functions provided below, so the 224 | // internals can be changed more easily. 225 | struct lexeme_s { 226 | TQ_ENT_FIELDS(struct lexeme_s) 227 | lextype_t type; 228 | lextype_t boundtype; 229 | strdesc_t text; 230 | unsigned long flags; 231 | void *extra; 232 | }; 233 | typedef struct lexeme_s lexeme_t; 234 | 235 | struct lexseq_s { 236 | TQ_HDR_FIELDS(lexeme_t) 237 | }; 238 | typedef struct lexseq_s lexseq_t; 239 | 240 | struct lexctx_s; 241 | typedef struct lexctx_s *lexctx_t; 242 | 243 | typedef int (*lextype_bind_fn)(lexctx_t lctx, void *pctx, quotelevel_t ql, 244 | quotemodifier_t qm, lextype_t lt, condstate_t cs, 245 | lexeme_t *orig, lexseq_t *result); 246 | 247 | lexctx_t lexeme_init(strctx_t strctx, logctx_t logctx); 248 | void lexeme_finish(lexctx_t lctx); 249 | lexeme_t *lexeme_copy(lexctx_t lctx, lexeme_t *orig); 250 | void lexeme_free(lexctx_t lctx, lexeme_t *lex); 251 | lexeme_t *lexeme_create(lexctx_t lctx, lextype_t type, strdesc_t *dsc); 252 | const char *lextype_name(lextype_t lt); 253 | 254 | int lextype_register(lexctx_t lctx, void *ctx, lextype_t lt, lextype_bind_fn bindfn); 255 | int lexeme_bind(lexctx_t lctx, textpos_t curpos, 256 | quotelevel_t ql, quotemodifier_t qm, 257 | condstate_t cs, lexeme_t *lex, lexseq_t *result); 258 | 259 | void lexseq_free(lexctx_t lctx, lexseq_t *seq); 260 | int lexseq_copy(lexctx_t lctx, lexseq_t *dst, lexseq_t *src); 261 | int lexemes_match(lexseq_t *a, lexseq_t *b); 262 | unsigned int lexseq_sersize(lexseq_t *seq); 263 | int lexseq_serialize(filectx_t fh, lexseq_t *seq); 264 | int lexseq_deserialize(lexctx_t lctx, filectx_t fh, unsigned int totsize, lexseq_t *seq); 265 | 266 | // Getters/settters for lexemes 267 | 268 | #undef siu 269 | #define siu static inline __attribute__((unused)) 270 | siu lexeme_t *lexeme_next (lexeme_t *lex) { return lex->tq_next; } 271 | siu lextype_t lexeme_boundtype (lexeme_t *lex) { return lex->boundtype; } 272 | siu lextype_t lexeme_type (lexeme_t *lex) { return lex->type; } 273 | siu void lexeme_type_set(lexeme_t *lex, lextype_t type) { 274 | lex->type = type; if (type != LEXTYPE_UNBOUND) lex->boundtype = type; } 275 | siu void lexeme_boundtype_set(lexeme_t *lex, lextype_t type) { lex->boundtype = type; } 276 | siu strdesc_t *lexeme_text (lexeme_t *lex) { return &lex->text; } 277 | siu void lexeme_text_set (lexeme_t *lex, strctx_t strctx, strdesc_t *newtext) { 278 | string_copy(strctx, &lex->text, newtext); } 279 | siu size_t lexeme_textlen(lexeme_t *lex) { return lex->text.len; } 280 | siu void *lexeme_ctx_get (lexeme_t *lex) { return lex->extra; } 281 | siu void lexeme_ctx_set (lexeme_t *lex, void *p) { lex->extra = p; } 282 | #undef siu 283 | 284 | DEFINE_TQ_FUNCS(lexseq, lexseq_t, lexeme_t) 285 | 286 | #endif /* lexeme_h__ */ 287 | -------------------------------------------------------------------------------- /lib/frontend/lexeme.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * lexeme.c - Lexeme handling 4 | * 5 | * This module implements the basic handling of lexemes. 6 | * It provides memory management of the lexeme pool, routines 7 | * for manipulating lexemes and lexeme sequences (along with the 8 | * inlined functions in the counterpart header file), and 9 | * the plug-in framework for lexical binding that the parser and other 10 | * modules use. 11 | * 12 | * Copyright © 2012-2020, Matthew Madison. 13 | * All rights reserved. 14 | * Distributed under license. See LICENSE.TXT for details. 15 | *-- 16 | */ 17 | #include 18 | #include "blissc/lexeme.h" 19 | 20 | #define DOLEXTYPE(lt) "LEXTYPE_" #lt, 21 | static const char *ltnames[] = { DOLEXTYPES }; 22 | #undef DOLEXTYPE 23 | 24 | #define ALLOC_QTY 512 25 | 26 | struct extenthdr_s { 27 | struct extenthdr_s *next; 28 | }; 29 | 30 | struct lexctx_s { 31 | logctx_t logctx; 32 | strctx_t strctx; 33 | struct extenthdr_s *extents; 34 | lexeme_t *freepool; 35 | lextype_bind_fn binders[LEXTYPE_COUNT]; 36 | void *bctx[LEXTYPE_COUNT]; 37 | }; 38 | 39 | static lexeme_t errlex = { .type = LEXTYPE_NONE }; 40 | 41 | /* 42 | * lextype_name 43 | * 44 | * Returns a string with the name of a lexeme type. 45 | * For debugging purposes only. 46 | */ 47 | const char * 48 | lextype_name (lextype_t lt) 49 | { 50 | if (lt < LEXTYPE_MIN || lt > LEXTYPE_MAX) { 51 | return "*LEXTYPE_OUTOFRANGE*"; 52 | } 53 | return ltnames[lt]; 54 | 55 | } /* lextype_name */ 56 | 57 | /* 58 | * lextype_register 59 | * 60 | * Registers a lexical binding function. 61 | */ 62 | int 63 | lextype_register (lexctx_t lctx, void *ctx, lextype_t lt, lextype_bind_fn binder) 64 | { 65 | if (lt < LEXTYPE_MIN || lt > LEXTYPE_MAX) { 66 | return 0; 67 | } 68 | lctx->binders[lt] = binder; 69 | lctx->bctx[lt] = ctx; 70 | return 1; 71 | 72 | } /* lextype_register */ 73 | 74 | /* 75 | * lexeme_alloc 76 | * 77 | * Internal allocation function. Expands the lookaside list 78 | * when needed. 79 | */ 80 | static lexeme_t * 81 | lexeme_alloc (lexctx_t lctx, lextype_t type, const char *text, size_t len) 82 | { 83 | lexeme_t *lex; 84 | int i; 85 | 86 | if (lctx->freepool == 0) { 87 | struct extenthdr_s *extent; 88 | extent = malloc(sizeof(struct extenthdr_s) + (ALLOC_QTY * sizeof(lexeme_t))); 89 | if (extent == 0) { 90 | log_signal(lctx->logctx, 0, STC__OUTOFMEM, "lexeme_alloc"); 91 | return &errlex; 92 | } 93 | extent->next = lctx->extents; 94 | lctx->extents = extent; 95 | lctx->freepool = (lexeme_t *)(extent + 1); 96 | for (i = 0, lex = lctx->freepool; i < ALLOC_QTY-1; i++, lex++) { 97 | lex->tq_next = lex + 1; 98 | } 99 | lex->tq_next = 0; 100 | } 101 | 102 | lex = lctx->freepool; 103 | lctx->freepool = lex->tq_next; 104 | memset(lex, 0, sizeof(lexeme_t)); 105 | string_alloc(lctx->strctx, &lex->text, len); 106 | if (text != 0) { 107 | string_from_chrs(lctx->strctx, &lex->text, text, len); 108 | } 109 | lex->type = lex->boundtype = type; 110 | lex->flags = LEX_M_ALLOCATED; 111 | return lex; 112 | } 113 | 114 | /* 115 | * lexeme_bind 116 | * 117 | * The core lexical binding function. If a module has registered 118 | * its own binder for a given lexeme type, it is responsible for 119 | * all handling of current lexical state -- conditional, quoting, 120 | * etc. For lexeme types that do not have a binder registered, 121 | * this routine performs the normal interpretation of lexical 122 | * state. This means that if you implement anything for which 123 | * those normal rules may not apply (e.g., a lexical function or 124 | * macro-related lexeme type), you MUST register a binding function 125 | * to handle the exceptional cases. DO NOT code any special-case 126 | * handling here. 127 | * 128 | * Returns: 129 | * -1: error 130 | * 0: binding/unbinding occurred normally, original 131 | * lexeme modified accordingly 132 | * 1: binding/unbinding resulted in modification of 133 | * lexeme sequence (could be null result) 134 | */ 135 | int 136 | lexeme_bind (lexctx_t lctx, textpos_t curpos, quotelevel_t ql, quotemodifier_t qm, 137 | condstate_t cs, lexeme_t *lex, lexseq_t *result) 138 | { 139 | lextype_t lt = lexeme_boundtype(lex); 140 | 141 | if (lctx->binders[lt] != 0) { 142 | return lctx->binders[lt](lctx, lctx->bctx[lt], ql, qm, lt, cs, lex, result); 143 | } 144 | 145 | // Check for lexical conditional skips 146 | if ((cs == COND_CWA && !(lt == LEXTYPE_LXF_ELSE || lt == LEXTYPE_LXF_FI || 147 | lt == LEXTYPE_LXF_IF)) || 148 | (cs == COND_AWC && !(lt == LEXTYPE_LXF_FI || lt == LEXTYPE_LXF_IF))) { 149 | lexeme_free(lctx, lex); 150 | return 1; 151 | } 152 | 153 | // Check for special 'macro skip' - which skips everything except 154 | // the macro-end marker and terminating of conditionals (the check for 155 | // which is above). 156 | if (ql == QL_MACROSKIP && cs == COND_NORMAL && lt != LEXTYPE_MACROEND ) { 157 | lexeme_free(lctx, lex); 158 | return 1; 159 | } 160 | 161 | if (qm == QM_QUOTE) { 162 | lex->type = LEXTYPE_UNBOUND; 163 | return 0; 164 | } 165 | 166 | if (lex->type != LEXTYPE_UNBOUND) { 167 | return 0; 168 | } 169 | 170 | if (lt == LEXTYPE_CSTRING) { 171 | strdesc_t *ltext = lexeme_text(lex); 172 | strdesc_t *cstr; 173 | lexeme_t *nlex; 174 | size_t len; 175 | 176 | len = ltext->len > 255 ? 255 : ltext->len; 177 | cstr = ascic_string_from_chrs(lctx->strctx, 0, ltext->ptr, len); 178 | nlex = lexeme_alloc(lctx, lt, cstr->ptr, cstr->len); 179 | lexseq_instail(result, nlex); 180 | lexeme_free(lctx, lex); 181 | return 1; 182 | } 183 | 184 | lex->type = lt; 185 | return 0; 186 | 187 | } /* lexeme_bind */ 188 | 189 | /* 190 | * lexeme_init 191 | * 192 | * Module initialization. Called from the lexer module. 193 | */ 194 | lexctx_t 195 | lexeme_init (strctx_t strctx, logctx_t logctx) 196 | { 197 | lexctx_t lctx = malloc(sizeof(struct lexctx_s)); 198 | 199 | if (lctx != 0) { 200 | memset(lctx, 0, sizeof(struct lexctx_s)); 201 | lctx->logctx = logctx; 202 | lctx->strctx = strctx; 203 | } 204 | return lctx; 205 | 206 | } /* lexeme_init */ 207 | 208 | /* 209 | * lexeme_finish 210 | * 211 | * Shutdown routine. 212 | */ 213 | void 214 | lexeme_finish (lexctx_t lctx) 215 | { 216 | struct extenthdr_s *e, *enext; 217 | if (lctx == 0) { 218 | return; 219 | } 220 | for (e = lctx->extents; e != 0; e = enext) { 221 | enext = e->next; 222 | free(e); 223 | } 224 | 225 | free(lctx); 226 | 227 | } /* lexeme_finish */ 228 | 229 | /* 230 | * lexeme_create 231 | * 232 | * Public API for creating a lexeme from some value. 233 | */ 234 | lexeme_t * 235 | lexeme_create (lexctx_t lctx, lextype_t type, strdesc_t *tok) 236 | { 237 | lexeme_t *lex; 238 | 239 | if (type < LEXTYPE_MIN || type > LEXTYPE_MAX) { 240 | log_signal(lctx->logctx, 0, STC__INTCMPERR, "lexeme_create"); 241 | return &errlex; 242 | } 243 | lex = lexeme_alloc(lctx, LEXTYPE_UNBOUND, tok->ptr, tok->len); 244 | if (lex->type == LEXTYPE_NONE) { 245 | return &errlex; 246 | } 247 | lex->boundtype = type; 248 | 249 | return lex; 250 | 251 | } /* lexeme_create */ 252 | 253 | /* 254 | * lexeme_free 255 | * 256 | * Frees a lexeme, returning it to the lookaside list 257 | * if it was dynamically allocated. It is OK to pass 258 | * a null pointer or a pointer to a static lexeme; those 259 | * will simply be ignored, and it simplifies logic elsewhere 260 | * to allow this. 261 | */ 262 | void 263 | lexeme_free (lexctx_t lctx, lexeme_t *lex) 264 | { 265 | if (lex == 0) { 266 | return; 267 | } 268 | if (lex->flags & LEX_M_ALLOCATED) { 269 | string_free(lctx->strctx, &lex->text); 270 | memset(lex, 0xdd, sizeof(lexeme_t)); 271 | lex->tq_next = lctx->freepool; 272 | lctx->freepool = lex; 273 | } 274 | 275 | } /* lexeme_free */ 276 | 277 | /* 278 | * lexeme_copy 279 | * 280 | * Allocates a new lexeme and copies the important 281 | * fields over from the original. 282 | */ 283 | lexeme_t * 284 | lexeme_copy (lexctx_t lctx, lexeme_t *orig) 285 | { 286 | lexeme_t *lex; 287 | 288 | if (orig == 0) { 289 | return 0; 290 | } 291 | lex = lexeme_alloc(lctx, orig->type, orig->text.ptr, 292 | (size_t)orig->text.len); 293 | if (lex == 0) { 294 | log_signal(lctx->logctx, 0, STC__INTCMPERR, "lexeme_copy"); 295 | return &errlex; 296 | } 297 | lex->boundtype = orig->boundtype; 298 | lex->extra = orig->extra; 299 | lex->tq_next = 0; 300 | return lex; 301 | 302 | } /* lexeme_copy */ 303 | 304 | /* 305 | * lexseq_free 306 | * 307 | * Frees a entire tail queue of lexemes. 308 | */ 309 | void 310 | lexseq_free (lexctx_t lctx, lexseq_t *seq) 311 | { 312 | lexeme_t *lex; 313 | for (lex = lexseq_remhead(seq); lex != 0; lex = lexseq_remhead(seq)) { 314 | lexeme_free(lctx, lex); 315 | } 316 | } /* lexseq_free */ 317 | 318 | /* 319 | * lexseq_copy 320 | * 321 | * Returns a duplicate of a lexeme sequence, 322 | * appended to the destination. 323 | */ 324 | int 325 | lexseq_copy (lexctx_t lctx, lexseq_t *dst, lexseq_t *src) 326 | { 327 | lexeme_t *lex; 328 | 329 | for (lex = lexseq_head(src); lex != 0; lex = lexeme_next(lex)) { 330 | lexseq_instail(dst, lexeme_copy(lctx, lex)); 331 | } 332 | 333 | return 1; 334 | 335 | } /* lexseq_copy */ 336 | 337 | /* 338 | * lexseq_sersize 339 | * 340 | * Computes the number of bytes required to serialize a 341 | * lexeme sequence. 342 | */ 343 | unsigned int 344 | lexseq_sersize (lexseq_t *seq) 345 | { 346 | lexeme_t *lex; 347 | unsigned int count = 0; 348 | 349 | for (lex = lexseq_head(seq); lex != 0; lex = lexeme_next(lex)) { 350 | strdesc_t *str = lexeme_text(lex); 351 | // type, boundtype, string length, string 352 | count += sizeof(uint16_t) + sizeof(uint16_t) + 353 | sizeof(uint16_t) + str->len; 354 | } 355 | return count; 356 | 357 | } /* lexseq_sersize */ 358 | 359 | /* 360 | * lexseq_serialize 361 | * 362 | * Serialize a lexeme sequence. 363 | */ 364 | int 365 | lexseq_serialize (filectx_t fh, lexseq_t *seq) 366 | { 367 | lexeme_t *lex; 368 | uint16_t hdr[3]; 369 | for (lex = lexseq_head(seq); lex != 0; lex = lexeme_next(lex)) { 370 | strdesc_t *str = lexeme_text(lex); 371 | hdr[0] = lexeme_type(lex); 372 | hdr[1] = lexeme_boundtype(lex); 373 | hdr[2] = (uint16_t) str->len; 374 | if (file_writebuf(fh, hdr, sizeof(hdr)) < 0) { 375 | return 0; 376 | } 377 | if (file_writebuf(fh, str->ptr, str->len) < 0) { 378 | return 0; 379 | } 380 | } 381 | return 1; 382 | 383 | } /* lexseq_serialize */ 384 | 385 | /* 386 | * lexseq_deserialize 387 | * 388 | * Reconstitute a serialized lexeme sequence. 389 | */ 390 | int 391 | lexseq_deserialize (lexctx_t lctx, filectx_t fh, unsigned int sersize, lexseq_t *seq) 392 | { 393 | lexeme_t *lex; 394 | uint16_t hdr[3]; 395 | char buf[256], *b; 396 | size_t len; 397 | 398 | while (sersize > 0) { 399 | if (file_readbuf(fh, hdr, sizeof(hdr), &len) <= 0 400 | || len != sizeof(hdr)) { 401 | return 0; 402 | } 403 | if (hdr[2] > sizeof(buf)) { 404 | b = malloc(hdr[2]); 405 | if (b == 0) return 0; 406 | } else { 407 | b = buf; 408 | } 409 | if (file_readbuf(fh, b, hdr[2], &len) <= 0 410 | || len != hdr[2]) { 411 | if (b != buf) free(b); 412 | return 0; 413 | } 414 | lex = lexeme_alloc(lctx, (lextype_t) hdr[0], b, len); 415 | if (b != buf) free(b); 416 | lex->boundtype = (lextype_t) hdr[1]; 417 | lexseq_instail(seq, lex); 418 | sersize -= sizeof(uint16_t) * 3 + hdr[2]; 419 | } 420 | 421 | return 1; 422 | 423 | } /* lexseq_deserialize */ 424 | 425 | /* 426 | * lexemes_match 427 | * 428 | * Compares two sequences of lexemes to see if they are 429 | * equivalent (e.g., for %IDENTICAL). That is, the 430 | * lextypes match and their strings match. 431 | */ 432 | int 433 | lexemes_match (lexseq_t *a, lexseq_t *b) 434 | { lexeme_t *la, *lb; 435 | if (lexseq_length(a) != lexseq_length(b)) { 436 | return 0; 437 | } 438 | for (la = lexseq_head(a), lb = lexseq_head(b); la != 0; 439 | la = la->tq_next, lb = lb->tq_next) { 440 | if (!strings_eql(&la->text, &lb->text)) { 441 | return 0; 442 | } 443 | } 444 | 445 | return 1; 446 | 447 | } /* lexemes_match */ 448 | -------------------------------------------------------------------------------- /lib/support/strings.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * strings.c - Dynamic string management. 4 | * 5 | * This module contains all of the memory management 6 | * for dynamic strings, along with functions for manipulating 7 | * strings via string descriptors. 8 | * 9 | * Note that descriptors are embedded in each string cell, 10 | * and the descriptor-based API will return pointers to 11 | * dynamically-allocated descriptors. However, callers 12 | * can create their own descriptors, if it's more 13 | * convenient to do so, and statically-allocated strings 14 | * (i.e., strings not allocated by this module) can be 15 | * referenced through descriptors. 16 | * 17 | * Strings are allocated out of memory pools. Three pools 18 | * are maintained here, 'small', 'medium', and 'large'. 19 | * When a request is made to allocate space for a string, 20 | * the length specified in the request is used to select the 21 | * pool, based on the SMALLSZ, MEDSZ, and LRGSZ definitions 22 | * below. Lookaside lists are kept with each pool, so 23 | * string cells will get reused after being freed. The 24 | * lists are pre-allocated with ALLOCOUNT entries at 25 | * initialization time. 26 | * 27 | * XXX The choice of sizes here is based on an educated guess 28 | * of string-size frequency in a typical compilation. Some 29 | * instrumentation and analysis is called for to tune the 30 | * cell sizes for each pool -- or to select a different 31 | * implementation, if that would provide a better balance 32 | * between heap usage/fragmentation and speed. 33 | * 34 | * 35 | * Copyright © 2012-2020, Matthew Madison. 36 | * All rights reserved. 37 | * Distributed under license. See LICENSE.TXT for details. 38 | *-- 39 | */ 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include "blissc/support/strings.h" 46 | 47 | #define POOL_NONE (-1) 48 | #define POOL_SMALL 0 49 | #define POOL_MEDIUM 1 50 | #define POOL_LARGE 2 51 | #define POOL_COUNT (POOL_LARGE+1) 52 | 53 | #define SMALLSZ 16 54 | #define MEDSZ 64 55 | #define LRGSZ STRING_MAXLEN 56 | 57 | #define ALLOCOUNT 256 58 | 59 | struct string_s { 60 | struct string_s *next; 61 | int poolindex; 62 | strdesc_t desc; 63 | }; 64 | typedef struct string_s string_t; 65 | 66 | struct stringpool_s { 67 | string_t *freelist; 68 | size_t maxsize; 69 | }; 70 | 71 | struct extenthdr_s { 72 | struct extenthdr_s *next; 73 | }; 74 | 75 | struct strctx_s { 76 | struct stringpool_s pool[POOL_COUNT]; 77 | struct extenthdr_s *extents; 78 | }; 79 | 80 | static struct stringpool_s pool[POOL_COUNT] = { 81 | { 0, SMALLSZ }, { 0, MEDSZ }, { 0, LRGSZ } 82 | }; 83 | 84 | /* 85 | * Internal utility routines. 86 | */ 87 | static inline int is_static (strdesc_t *dsc) { 88 | return ((dsc->flags & STR_M_STATIC) != 0); 89 | } 90 | 91 | static inline int size_to_pool (size_t len) { 92 | int i; 93 | for (i = 0; i < POOL_COUNT; i++) { 94 | if (len <= pool[i].maxsize) { 95 | return i; 96 | } 97 | } 98 | return POOL_NONE; 99 | } 100 | 101 | static inline string_t *desc_to_str (strdesc_t *dsc) { 102 | return is_static(dsc) ? 0 : (string_t *)(dsc->ptr - sizeof(string_t)); 103 | } 104 | 105 | 106 | /* 107 | * stringpool_expand 108 | * 109 | * Add more entries to a string pool's lookaside list. 110 | */ 111 | static int 112 | stringpool_expand (strctx_t strctx, int idx) 113 | { 114 | size_t maxsize = strctx->pool[idx].maxsize; 115 | struct extenthdr_s *extent; 116 | string_t *more; 117 | string_t *str = 0; 118 | char *cp; 119 | int i; 120 | 121 | extent = malloc(sizeof(struct extenthdr_s) + (maxsize + sizeof(string_t))*ALLOCOUNT); 122 | if (extent == 0) { 123 | return 0; 124 | } 125 | extent->next = strctx->extents; 126 | strctx->extents = extent; 127 | more = (string_t *)(extent + 1); 128 | 129 | for (cp = (char *)more, i = 0; i < ALLOCOUNT; 130 | i++, cp += pool[idx].maxsize) { 131 | str = (string_t *) cp; 132 | cp += sizeof(string_t); 133 | str->poolindex = idx; 134 | str->next = (string_t *)(cp + maxsize); 135 | str->desc.flags = 0; 136 | str->desc.len = 0; 137 | str->desc.ptr = cp; 138 | } 139 | // on exit, 'str' still points to the last allocated entry 140 | str->next = strctx->pool[idx].freelist; 141 | strctx->pool[idx].freelist = more; 142 | return 1; 143 | 144 | } /* stringpool_expand */ 145 | 146 | 147 | /* 148 | * strings_init 149 | * 150 | * Initialize the string pools. 151 | */ 152 | strctx_t 153 | strings_init (void) 154 | { 155 | int i; 156 | strctx_t sctx = malloc(sizeof(struct strctx_s)); 157 | 158 | if (sctx != 0) { 159 | memset(sctx, 0, sizeof(struct strctx_s)); 160 | sctx->pool[0].maxsize = SMALLSZ; 161 | sctx->pool[1].maxsize = MEDSZ; 162 | sctx->pool[2].maxsize = LRGSZ; 163 | for (i = 0; i < POOL_COUNT; i++) { 164 | if (!stringpool_expand(sctx, i)) { 165 | return 0; 166 | } 167 | } 168 | } 169 | return sctx; 170 | 171 | } /* strings_init */ 172 | 173 | /* 174 | * strings_finish 175 | * 176 | * Release all string memory. 177 | */ 178 | void 179 | strings_finish (strctx_t strctx) 180 | { 181 | struct extenthdr_s *e, *enext; 182 | 183 | if (strctx == 0) { 184 | return; 185 | } 186 | 187 | for (e = strctx->extents; e != 0; e = enext) { 188 | enext = e->next; 189 | free(e); 190 | } 191 | 192 | free(strctx); 193 | 194 | } /* strings_finish */ 195 | 196 | /* 197 | * string___alloc 198 | * 199 | * Internal allocation routine. Identifies 200 | * the string pool and triggers its expansion, 201 | * if needed. 202 | */ 203 | static string_t * 204 | string___alloc (strctx_t strctx, size_t len) 205 | { 206 | string_t *str; 207 | int i = size_to_pool(len); 208 | 209 | if (i == POOL_NONE) { 210 | return 0; 211 | } 212 | if (strctx->pool[i].freelist == 0) { 213 | if (!stringpool_expand(strctx, i)) { 214 | return 0; 215 | } 216 | } 217 | str = strctx->pool[i].freelist; 218 | strctx->pool[i].freelist = str->next; 219 | str->next = 0; 220 | return str; 221 | 222 | } /* string___alloc */ 223 | 224 | /* 225 | * string___free 226 | * 227 | * Return a string cell to its pool's lookaside list. 228 | */ 229 | static void 230 | string___free (strctx_t strctx, string_t *str) 231 | { 232 | if (str->poolindex == POOL_NONE) { 233 | return; 234 | } 235 | str->next = strctx->pool[str->poolindex].freelist; 236 | strctx->pool[str->poolindex].freelist = str; 237 | 238 | } /* string___free */ 239 | 240 | /* 241 | * string_from_chrs 242 | * 243 | * Allocates a dynamic string from a C-style character pointer/ 244 | * length pair, and returns a pointer to a descriptor for the 245 | * string. 246 | * 247 | */ 248 | strdesc_t * 249 | string_from_chrs (strctx_t strctx, strdesc_t *dest, const char *cp, size_t len) 250 | { 251 | string_t *str = string___alloc(strctx, len); 252 | 253 | if (str == 0) { 254 | return 0; 255 | } 256 | if (dest == 0) { 257 | dest = &str->desc; 258 | } else { 259 | dest->ptr = str->desc.ptr; 260 | dest->flags = 0; 261 | } 262 | dest->len = len; 263 | memcpy(dest->ptr, cp, len); 264 | return dest; 265 | 266 | } /* string_from_chrs */ 267 | 268 | /* 269 | * ascic_string_from_chrs 270 | * 271 | * Allocates a dynamic string in %ASCIC form - that is, 272 | * prefixed with a length byte. 273 | */ 274 | strdesc_t * 275 | ascic_string_from_chrs (strctx_t strctx, strdesc_t *dest, const char *cp, size_t len) 276 | { 277 | string_t *str = string___alloc(strctx, len+1); 278 | if (str == 0) { 279 | return 0; 280 | } 281 | if (dest == 0) { 282 | dest = &str->desc; 283 | } else { 284 | dest->ptr = str->desc.ptr; 285 | dest->flags = 0; 286 | } 287 | dest->len = len+1; 288 | memcpy(dest->ptr+1, cp, len); 289 | *(dest->ptr) = (char) (len & 0xff); 290 | return dest; 291 | 292 | } /* ascic_string_from_chrs */ 293 | 294 | /* 295 | * string_append 296 | * 297 | * Appends one string to another. Will allocate 298 | * a new string (and descriptor) if the concatenated 299 | * string is too long to fit into the destination's 300 | * current cell size, so callers must use the pointer 301 | * returned by this routine to access the result. 302 | */ 303 | strdesc_t * 304 | string_append (strctx_t strctx, strdesc_t *trg, strdesc_t *add) 305 | { 306 | string_t *str; 307 | string_t *newstr; 308 | 309 | if (trg == 0) { 310 | return string_copy(strctx, trg, add); 311 | } 312 | str = desc_to_str(trg); 313 | if (is_static(trg) || 314 | (trg->len + add->len <= pool[str->poolindex].maxsize)) { 315 | memcpy(trg->ptr + trg->len, add->ptr, add->len); 316 | trg->len += add->len; 317 | return trg; 318 | } 319 | newstr = string___alloc(strctx, trg->len + add->len); 320 | if (newstr == 0) { 321 | return 0; 322 | } 323 | newstr->desc.len = trg->len + add->len; 324 | memcpy(newstr->desc.ptr, trg->ptr, trg->len); 325 | memcpy(newstr->desc.ptr + trg->len, add->ptr, add->len); 326 | string___free(strctx, str); 327 | return &newstr->desc; 328 | 329 | } /* string_append */ 330 | 331 | /* 332 | * string_free 333 | * 334 | * Public API for freeing a string via a string 335 | * descriptor. It's OK to pass a null pointer or 336 | * a pointer to a static string descriptor; they will 337 | * be silently ignored. 338 | */ 339 | void 340 | string_free (strctx_t strctx, strdesc_t *dsc) 341 | { 342 | if (dsc == 0 || is_static(dsc)) { 343 | return; 344 | } 345 | string___free(strctx, desc_to_str(dsc)); 346 | 347 | } /* string_free */ 348 | 349 | /* 350 | * string_alloc 351 | * 352 | * Public API for allocating a string and descriptor 353 | * for a string cell that will hold at least 'len' 354 | * characters. If a destination descriptor is 355 | * provided, it will be set to point to the allocated 356 | * string. 357 | */ 358 | strdesc_t * 359 | string_alloc (strctx_t strctx, strdesc_t *dest, size_t len) 360 | { 361 | string_t *str = string___alloc(strctx, len); 362 | if (str == 0) { 363 | return 0; 364 | } 365 | if (dest == 0) { 366 | dest = &str->desc; 367 | } 368 | dest->flags = 0; 369 | dest->ptr = str->desc.ptr; 370 | dest->len = len; 371 | return dest; 372 | 373 | } /* string_alloc */ 374 | 375 | /* 376 | * string_copy 377 | * 378 | * Makes a copy of a string. Returns a pointer 379 | * to the copy's descriptor (which will be the 'dest' 380 | * descriptor, if 'dest' is non-null). 381 | */ 382 | strdesc_t * 383 | string_copy (strctx_t strctx, strdesc_t *dest, strdesc_t *src) 384 | { 385 | string_t *str; 386 | 387 | if (dest != 0 && !is_static(dest)) { 388 | str = desc_to_str(dest); 389 | if (strctx->pool[str->poolindex].maxsize >= src->len) { 390 | dest->len = src->len; 391 | memcpy(dest->ptr, src->ptr, src->len); 392 | return dest; 393 | } 394 | string___free(strctx, str); 395 | } 396 | str = string___alloc(strctx, src->len); 397 | if (str == 0) { 398 | return 0; 399 | } 400 | if (dest == 0) { 401 | dest = &str->desc; 402 | } else { 403 | dest->ptr = str->desc.ptr; 404 | } 405 | dest->len = src->len; 406 | memcpy(dest->ptr, src->ptr, src->len); 407 | return dest; 408 | 409 | } /* string_copy */ 410 | 411 | /* 412 | * strings_eql 413 | * 414 | * Returns 1 if the two strings are the same 415 | * length and compare for equality, zero otherwise. 416 | */ 417 | int 418 | strings_eql (strdesc_t *a, strdesc_t *b) 419 | { 420 | if (a->len != b->len) { 421 | return 0; 422 | } 423 | return memcmp(a->ptr, b->ptr, a->len) == 0; 424 | 425 | } /* strings_eql */ 426 | 427 | /* 428 | * string_printf 429 | * 430 | * Provides printf() formatting into a dynamically 431 | * allocated string. Note that to keep things simple, 432 | * the sprintf() is done into a local buffer first, 433 | * and then copied to the dynamic string. 434 | */ 435 | strdesc_t * 436 | string_printf (strctx_t strctx, strdesc_t *dst, const char *fmt, ...) 437 | { 438 | va_list ap; 439 | char buf[256]; 440 | int len; 441 | 442 | va_start(ap, fmt); 443 | len = vsnprintf(buf, sizeof(buf), fmt, ap); 444 | va_end(ap); 445 | return string_from_chrs(strctx, dst, buf, (size_t) (len < 0 ? 0 : len)); 446 | 447 | } /* string_printf */ 448 | 449 | /* 450 | * string_numval 451 | * 452 | * Uses strtol() to parse a numeric value from a string. 453 | * Any radix value supported by strtol() may be specified 454 | * for 'base', but typical values are 8, 10, and 16. 455 | */ 456 | int 457 | string_numval (strdesc_t *str, int base, long *valp) 458 | { 459 | char buf[32], *cp; 460 | long numval; 461 | 462 | if (str->len >= sizeof(buf)) { 463 | return 0; 464 | } 465 | memcpy(buf, str->ptr, str->len); 466 | buf[str->len] = '\0'; 467 | errno = 0; 468 | numval = strtoul(buf, &cp, base); 469 | if (errno != 0 || (cp-buf) != (long) str->len) { 470 | return 0; 471 | } 472 | *valp = numval; 473 | return 1; 474 | 475 | } /* string_numval */ 476 | -------------------------------------------------------------------------------- /lib/driver/driver.c: -------------------------------------------------------------------------------- 1 | /* 2 | *++ 3 | * driver.c - Compiler "driver" interface 4 | * 5 | * This module implements the upper edge of the compiler, 6 | * providing a callable interface to specify compiler options 7 | * and exposing these options to other parts of the compiler. 8 | * 9 | * Copyright © 2013-2024, Matthew Madison. 10 | * All rights reserved. 11 | * Distributed under license. See LICENSE.TXT for details. 12 | *-- 13 | */ 14 | #include "blissc/driver.h" 15 | #include "blissc/support/strings.h" 16 | #include "blissc/support/logging.h" 17 | #include "blissc/support/fileio.h" 18 | #include "blissc/machinedef.h" 19 | #include "blissc/gencode.h" 20 | #include "blissc/declarations.h" 21 | #include "blissc/libgen.h" 22 | #include "blissc/config.h" 23 | 24 | #ifndef PACKAGE_NAME 25 | #define PACKAGE_NAME "blissc" 26 | #endif 27 | #ifndef PACKAGE_VERSION 28 | #define PACKAGE_VERSION "" 29 | #endif 30 | 31 | static char const package_name[] = PACKAGE_NAME; 32 | static char const package_version[] = PACKAGE_VERSION; 33 | 34 | #define MAXPATHS 32 35 | 36 | struct blissc_driverctx_s { 37 | strctx_t strctx; 38 | logctx_t logctx; 39 | fioctx_t fioctx; 40 | parse_ctx_t pctx; 41 | expr_ctx_t ectx; 42 | libgen_ctx_t lgctx; 43 | machinedef_t *mach; 44 | scopectx_t kwdscope; 45 | unsigned int variant; 46 | unsigned int listflags; 47 | bliss_output_t outtype; 48 | char *outfn; 49 | size_t outfnlen; 50 | int free_outfn; 51 | char *listfn; 52 | size_t listfnlen; 53 | int free_listfn; 54 | int optlevel; 55 | int dumpir; 56 | char *irfn; 57 | size_t irfnlen; 58 | int free_irfn; 59 | unsigned int pathcount; 60 | char *paths[MAXPATHS]; 61 | }; 62 | 63 | static inline size_t 64 | filename_length (const char *fname, int fnlen) { 65 | if (fnlen < 0) 66 | return strlen(fname); 67 | return (size_t) fnlen; 68 | } 69 | 70 | /* 71 | * blissc_init 72 | * 73 | * Initializes the driver, setting up the context block. 74 | * This should be first routine called by a driver program. 75 | */ 76 | blissc_driverctx_t 77 | blissc_init (jmp_buf retenv) 78 | { 79 | blissc_driverctx_t ctx; 80 | 81 | ctx = malloc(sizeof(struct blissc_driverctx_s)); 82 | if (ctx == 0) return 0; 83 | memset(ctx, 0, sizeof(struct blissc_driverctx_s)); 84 | ctx->strctx = strings_init(); 85 | ctx->logctx = logging_init(retenv); 86 | ctx->fioctx = fileio_init(ctx->logctx); 87 | ctx->outtype = BLISS_K_OUTPUT_OBJECT; 88 | ctx->optlevel = -1; // unset 89 | 90 | return ctx; 91 | 92 | } /* blissc_init */ 93 | 94 | /* 95 | * blissc_package_name 96 | * 97 | * Returns the name of this package. 98 | */ 99 | const char * 100 | blissc_package_name (void) 101 | { 102 | return package_name; 103 | 104 | } /* blissc_package_name */ 105 | 106 | /* 107 | * blissc_package_version 108 | * 109 | * Returns the version of this package. 110 | */ 111 | const char * 112 | blissc_package_version (void) 113 | { 114 | return package_version; 115 | 116 | } /* blissc_package_version */ 117 | 118 | /* 119 | * blissc_output_set 120 | * 121 | * Sets the output format and, optionally, the output 122 | * file name. 123 | */ 124 | int 125 | blissc_output_set (blissc_driverctx_t ctx, bliss_output_t outtype, 126 | const char *fname, int fnlen_p) 127 | { 128 | ctx->outtype = outtype; 129 | if (fname != 0) { 130 | size_t fnlen = filename_length(fname, fnlen_p); 131 | ctx->outfn = malloc(fnlen+1); 132 | if (ctx->outfn == 0) return 0; 133 | memcpy(ctx->outfn, fname, fnlen); 134 | ctx->outfn[fnlen] = '\0'; 135 | ctx->outfnlen = fnlen; 136 | ctx->free_outfn = 1; 137 | } 138 | return 1; 139 | 140 | } /* blissc_output_set */ 141 | 142 | /* 143 | * blissc_listopt_set 144 | * 145 | * Sets the listing options and, optionally, 146 | * the listing file name. 147 | */ 148 | int 149 | blissc_listopt_set (blissc_driverctx_t ctx, unsigned int flags, 150 | const char *fname, int fnlen_p) 151 | { 152 | if (ctx->outtype == BLISS_K_OUTPUT_LIBRARY) return 0; 153 | 154 | ctx->listflags = flags; 155 | if (fname != 0) { 156 | size_t fnlen = filename_length(fname, fnlen_p); 157 | ctx->listfn = malloc(fnlen); 158 | if (ctx->listfn == 0) return 0; 159 | memcpy(ctx->listfn, fname, fnlen); 160 | ctx->listfn[fnlen] = '\0'; 161 | ctx->listfnlen = fnlen; 162 | ctx->free_listfn = 1; 163 | } 164 | return 1; 165 | 166 | } /* blissc_listopt_set */ 167 | 168 | /* 169 | * Sets the option to dump the IR output to a file 170 | * and, optionally, the filename for that output. 171 | */ 172 | int 173 | blissc_dumpir_set (blissc_driverctx_t ctx, int val, 174 | const char *fname, int fnlen_p) 175 | { 176 | if (ctx->outtype == BLISS_K_OUTPUT_LIBRARY) return 0; 177 | 178 | ctx->dumpir = val; 179 | if (fname != 0) { 180 | size_t fnlen = filename_length(fname, fnlen_p); 181 | ctx->irfn = malloc(fnlen); 182 | if (ctx->irfn == 0) return 0; 183 | memcpy(ctx->irfn, fname, fnlen); 184 | ctx->irfn[fnlen] = '\0'; 185 | ctx->irfnlen = fnlen; 186 | ctx->free_irfn = 1; 187 | } 188 | return 1; 189 | 190 | } /* blissc_dumpir_set */ 191 | 192 | /* 193 | * blissc_variant_set 194 | * 195 | * Sets the value for the %VARIANT lexical 196 | * function. 197 | */ 198 | int blissc_variant_set (blissc_driverctx_t ctx, unsigned int val) 199 | { 200 | ctx->variant = val; 201 | return 1; 202 | 203 | } /* blissc_variant_set */ 204 | 205 | /* 206 | * blissc_target_set 207 | * 208 | * Sets the target tuple string for the compiler. 209 | */ 210 | int 211 | blissc_target_set (blissc_driverctx_t ctx, const char *machspec) 212 | { 213 | ctx->mach = machine_init(machspec); 214 | if (ctx->mach == 0) return 0; 215 | ctx->pctx = parser_init(ctx->strctx, 0, ctx->mach, &ctx->kwdscope, ctx->logctx, ctx->fioctx); 216 | ctx->ectx = expr_init(ctx->strctx, ctx->pctx, ctx->kwdscope); 217 | return 1; 218 | 219 | } /* blissc_target_set */ 220 | 221 | /* 222 | * blissc_optlevel_set 223 | * 224 | * Sets the optimization level. 225 | */ 226 | int 227 | blissc_optlevel_set (blissc_driverctx_t ctx, unsigned int val) 228 | { 229 | ctx->optlevel = val; 230 | return 1; 231 | 232 | } /* blissc_optlevel_set */ 233 | 234 | /* 235 | * blissc_searchpath_add 236 | * 237 | * Adds a search path for REQUIRE/LIBARARY file lookups. The 238 | * compiler needs this to be formatted as a directory name, so 239 | * we make sure that it ends with the requisite '/'. 240 | * 241 | * XXX - should we check for the path's existence here? 242 | */ 243 | int 244 | blissc_searchpath_add (blissc_driverctx_t ctx, const char *path, int pathlen) 245 | { 246 | size_t len = filename_length(path, pathlen); 247 | int needsterm = (path[len-1] != '/'); 248 | char *p; 249 | 250 | if (ctx->pathcount >= MAXPATHS) return 0; 251 | p = malloc(len+needsterm+1); 252 | memcpy(p, path, len); 253 | if (needsterm) p[len++] = '/'; 254 | p[len] = '\0'; 255 | ctx->paths[ctx->pathcount++] = p; 256 | return 1; 257 | 258 | } /* blissc_searchpath_add */ 259 | 260 | /* 261 | * blissc_compile 262 | * 263 | * Begins a compilation. This should be called only 264 | * after all of the desired compilation options have 265 | * been set -- they are processed here (passing the 266 | * settings down to the compiler modules) before 267 | * compilation begins. The source file name is required. 268 | */ 269 | int 270 | blissc_compile (blissc_driverctx_t ctx, const char *fname, int fnlen) 271 | { 272 | int status; 273 | size_t len = filename_length(fname, fnlen); 274 | fio_pathparts_t srcparts, objparts, lstparts; 275 | compilerinfo_t compilerinfo; 276 | unsigned int i; 277 | 278 | if (!file_splitname(ctx->fioctx, fname, fnlen, 1, &srcparts)) { 279 | return 0; 280 | } 281 | 282 | memset(&compilerinfo, 0, sizeof(compilerinfo)); 283 | compilerinfo.ver_major = BLISSC_VERSION_MAJOR; 284 | compilerinfo.ver_minor = BLISSC_VERSION_MINOR; 285 | compilerinfo.host_triple = BLISSC_HOST_TRIPLE; 286 | parser_compilerinfo_set(ctx->pctx, &compilerinfo); 287 | 288 | for (i = 0; i < ctx->pathcount; i++) { 289 | strdesc_t dsc; 290 | strdesc_init(&dsc, ctx->paths[i], strlen(ctx->paths[i])); 291 | if (!parser_searchpath_add(ctx->pctx, &dsc)) return 0; 292 | } 293 | 294 | if (ctx->variant != 0) parser_variant_set(ctx->pctx, ctx->variant); 295 | if (ctx->outfn == 0) { 296 | file_splitname(ctx->fioctx, srcparts.path_fullname, 297 | (int) srcparts.path_fullnamelen, 0, &objparts); 298 | if (ctx->outtype == BLISS_K_OUTPUT_ASSEMBLY) { 299 | objparts.path_suffix = ".s"; 300 | objparts.path_suffixlen = 2; 301 | } else if (ctx->outtype == BLISS_K_OUTPUT_OBJECT) { 302 | objparts.path_suffix = ".o"; 303 | objparts.path_suffixlen = 2; 304 | } else { // BLISS_K_OUTPUT_LIBRARY 305 | objparts.path_suffix = ".lib"; 306 | objparts.path_suffixlen = 4; 307 | } 308 | objparts.path_dirnamelen = 0; 309 | if (!file_combinename(ctx->fioctx, &objparts)) { 310 | return 0; 311 | } 312 | free(ctx->outfn); 313 | ctx->outfn = objparts.path_fullname; 314 | ctx->outfnlen = (unsigned int) objparts.path_fullnamelen; 315 | ctx->free_outfn = 1; 316 | } 317 | free(srcparts.path_fullname); 318 | if (ctx->outtype == BLISS_K_OUTPUT_LIBRARY) { 319 | ctx->lgctx = libgen_init(ctx->fioctx, ctx->outfn, ctx->outfnlen, 320 | &compilerinfo); 321 | } else { 322 | machine_output_t mo = (ctx->outtype == BLISS_K_OUTPUT_ASSEMBLY 323 | ? MACH_K_OUTPUT_ASM : MACH_K_OUTPUT_OBJ); 324 | machine_output_set(ctx->mach, mo, ctx->outfn, ctx->outfnlen); 325 | } 326 | 327 | if (ctx->listflags == 0) { 328 | if (ctx->listfn != 0 && ctx->free_listfn) free(ctx->listfn); 329 | ctx->listfn = 0; 330 | ctx->listfnlen = 0; 331 | } else { 332 | if (ctx->listfn == 0) { 333 | file_splitname(ctx->fioctx, ctx->outfn, ctx->outfnlen, 0, &lstparts); 334 | lstparts.path_suffix = ".lis"; 335 | lstparts.path_suffixlen = 4; 336 | } else { 337 | if (!file_splitname(ctx->fioctx, ctx->listfn, -1, 0, &lstparts)) { 338 | return 0; 339 | } 340 | if (lstparts.path_dirnamelen == 0) { 341 | file_splitname(ctx->fioctx, ctx->outfn, ctx->outfnlen, 0, &objparts); 342 | lstparts.path_dirnamelen = objparts.path_dirnamelen; 343 | lstparts.path_dirname = objparts.path_dirname; 344 | free(objparts.path_fullname); 345 | } 346 | } 347 | if (!file_combinename(ctx->fioctx, &lstparts)) { 348 | return 0; 349 | } 350 | if (ctx->listfn != 0 && ctx->free_listfn) free(ctx->listfn); 351 | ctx->listfn = lstparts.path_fullname; 352 | ctx->listfnlen = (unsigned int) lstparts.path_fullnamelen; 353 | ctx->free_listfn = 1; 354 | } 355 | 356 | if (ctx->dumpir) { 357 | if (ctx->irfn == 0) { 358 | file_splitname(ctx->fioctx, ctx->outfn, ctx->outfnlen, 0, &lstparts); 359 | lstparts.path_suffix = ".ll"; 360 | lstparts.path_suffixlen = 3; 361 | } else { 362 | if (!file_splitname(ctx->fioctx, ctx->irfn, ctx->irfnlen, 0, &lstparts)) { 363 | return 0; 364 | } 365 | } 366 | if (!file_combinename(ctx->fioctx, &lstparts)) { 367 | return 0; 368 | } 369 | if (ctx->irfn != 0 && ctx->free_irfn) free(ctx->irfn); 370 | ctx->irfn = lstparts.path_fullname; 371 | ctx->irfnlen = (unsigned int) lstparts.path_fullnamelen; 372 | ctx->free_irfn = 1; 373 | } 374 | machine_dumpir_set(ctx->mach, ctx->irfn, ctx->irfnlen); 375 | 376 | if (ctx->optlevel >= 0) { 377 | gencode_optlevel_set(expr_gencodectx(ctx->ectx), (unsigned int) ctx->optlevel); 378 | } 379 | status = parser_fopen_main(ctx->pctx, fname, len, 380 | ctx->listflags, ctx->listfn, ctx->listfnlen); 381 | if (status) { 382 | status = (ctx->outtype == BLISS_K_OUTPUT_LIBRARY 383 | ? libgen_parse(ctx->lgctx, ctx->ectx) 384 | : declare_module(ctx->ectx)); 385 | } 386 | 387 | return status; 388 | 389 | } /* blissc_compile */ 390 | 391 | /* 392 | * blissc_finish 393 | * 394 | * Cleans up and frees the compilation context. 395 | */ 396 | void 397 | blissc_finish (blissc_driverctx_t ctx) 398 | { 399 | unsigned int i; 400 | for (i = 0; i < ctx->pathcount; i++) { 401 | free(ctx->paths[i]); 402 | } 403 | if (ctx->lgctx != 0) libgen_finish(ctx->lgctx); 404 | if (ctx->ectx != 0) expr_finish(ctx->ectx); // which also calls parser_finish() 405 | if (ctx->mach) machine_finish(ctx->mach); 406 | if (ctx->fioctx != 0) fileio_finish(ctx->fioctx); 407 | if (ctx->logctx != 0) logging_finish(ctx->logctx); 408 | if (ctx->strctx != 0) strings_finish(ctx->strctx); 409 | if (ctx->free_listfn) free(ctx->listfn); 410 | if (ctx->free_outfn) free(ctx->outfn); 411 | if (ctx->free_irfn) free(ctx->irfn); 412 | free(ctx); 413 | 414 | } /* blissc_finish */ 415 | --------------------------------------------------------------------------------