Skip to content

Commit

Permalink
1. added @error and @warning preprocessor directives
Browse files Browse the repository at this point in the history
2. added -D name[=value] command-line options that the compiler transforms into one or more '@define name (value|true)' directives
3. fixed a few bugs in the preprocessor
4. cleaned the use of @ccode in functions a bit. '=' before @ccode {} is optional, so it was removed in most places.
  • Loading branch information
vpisarev committed Nov 29, 2021
1 parent c688c6f commit e9d06fe
Show file tree
Hide file tree
Showing 21 changed files with 475 additions and 390 deletions.
2 changes: 1 addition & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Petr Chekmarev (@4ekmah) multiple improvements and bug fixes in the compiler and the standard library,
Petr Chekmarev (@4ekmah) multiple improvements and bug fixes in the compiler and in the standard library;
array processing optimizations, re2 module
Vadim Pisarevsky (@vpisarev) the language architect and lead developer
26 changes: 13 additions & 13 deletions compiler/Ast.fx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ type binary_t =
| OpDotMul | OpDotDiv | OpDotMod | OpDotPow
| OpBitwiseAnd | OpLogicAnd | OpBitwiseOr | OpLogicOr | OpBitwiseXor
| OpCmp: cmpop_t | OpDotCmp: cmpop_t | OpSpaceship | OpDotSpaceship | OpSame | OpCons
| OpAugBinary: binary_t
| OpAugBinary: binary_t

type unary_t = OpPlus | OpNegate | OpDotMinus | OpBitwiseNot | OpLogicNot
| OpMkRef | OpDeref | OpExpand | OpApos
Expand Down Expand Up @@ -1039,9 +1039,9 @@ fun fname_op_dot_minus() = get_id("__dot_minus__")
fun fname_op_bit_not() = get_id("__bit_not__")
fun fname_op_aug_add() = get_id("__aug_add__")
fun fname_op_aug_sub() = get_id("__aug_sub__")
fun fname_op_aug_mul() = get_id("__aug_mul__")
fun fname_op_aug_div() = get_id("__aug_div__")
fun fname_op_aug_mod() = get_id("__aug_mod__")
fun fname_op_aug_mul() = get_id("__aug_mul__")
fun fname_op_aug_div() = get_id("__aug_div__")
fun fname_op_aug_mod() = get_id("__aug_mod__")
fun fname_op_aug_bit_and() = get_id("__aug_bit_and__")
fun fname_op_aug_bit_or() = get_id("__aug_bit_or__")
fun fname_op_aug_bit_xor() = get_id("__aug_bit_xor__")
Expand Down Expand Up @@ -1114,10 +1114,10 @@ fun get_binary_fname(bop: binary_t, loc: loc_t) =
| OpDotCmp(CmpGE) => fname_op_dot_ge()
| OpDotCmp(CmpGT) => fname_op_dot_gt()
| OpAugBinary(OpAdd) => fname_op_aug_add()
| OpAugBinary(OpSub) => fname_op_aug_sub()
| OpAugBinary(OpMul) => fname_op_aug_mul()
| OpAugBinary(OpDiv) => fname_op_aug_div()
| OpAugBinary(OpMod) => fname_op_aug_mod()
| OpAugBinary(OpSub) => fname_op_aug_sub()
| OpAugBinary(OpMul) => fname_op_aug_mul()
| OpAugBinary(OpDiv) => fname_op_aug_div()
| OpAugBinary(OpMod) => fname_op_aug_mod()
| OpAugBinary(OpBitwiseAnd) => fname_op_aug_bit_and()
| OpAugBinary(OpBitwiseOr) => fname_op_aug_bit_or()
| OpAugBinary(OpBitwiseXor) => fname_op_aug_bit_xor()
Expand Down Expand Up @@ -1153,12 +1153,12 @@ fun fname_always_import(): id_t list = [
fname_op_dot_eq(), fname_op_dot_ne(), fname_op_dot_le(),
fname_op_dot_ge(), fname_op_dot_lt(), fname_op_dot_gt(),
fname_op_plus(), fname_op_negate(), fname_op_dot_minus(),
fname_op_bit_not(), fname_op_aug_add(), fname_op_aug_sub(),
fname_op_aug_mul(), fname_op_aug_div(), fname_op_aug_mod(),
fname_op_bit_not(), fname_op_aug_add(), fname_op_aug_sub(),
fname_op_aug_mul(), fname_op_aug_div(), fname_op_aug_mod(),
fname_op_aug_bit_and(), fname_op_aug_bit_or(), fname_op_aug_bit_xor(),
fname_op_aug_dot_mul(), fname_op_aug_dot_div(), fname_op_aug_dot_mod(),
fname_op_aug_shl(), fname_op_aug_shr(),

fname_op_apos(),
fname_to_int(), fname_to_uint8(), fname_to_int8(), fname_to_uint16(),
fname_to_int16(), fname_to_uint32(), fname_to_int32(), fname_to_uint64(),
Expand Down Expand Up @@ -1229,8 +1229,8 @@ fun lit2str(c: lit_t) {
}
}

fun ref2str(r: 't ref): string = @ccode
{
fun ref2str(r: 't ref): string
@ccode {
char buf[32];
sprintf(buf, "%p", r);
return fx_cstr2str(buf, -1, fx_result);
Expand Down
9 changes: 9 additions & 0 deletions compiler/Compiler.fx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ fun clrmsg(clr: msgcolor_t, msg: string)
val error = clrmsg(MsgRed, "error")

fun get_preamble(mfname: string): Lexer.token_t list {
val preamble =
if Options.opt.use_preamble {
val bare_name = Filename.remove_extension(Filename.basename(mfname))
val (preamble, _) = fold (preamble, found) = ([], false)
Expand All @@ -63,6 +64,14 @@ fun get_preamble(mfname: string): Lexer.token_t list {
}
preamble
} else { [] }
fold p=preamble for (n, v) <- Options.opt.defines {
val v = match v {
| Options.OptBool(b) => Ast.LitBool(b)
| Options.OptInt(i) => Ast.LitInt(int64(i))
| Options.OptString(s) => Ast.LitString(s)
}
Lexer.PP_DEFINE :: Lexer.IDENT(true, n) :: Lexer.LITERAL(v) :: p
}
}

fun find_ficus_dirs(): (string, string list)
Expand Down
2 changes: 1 addition & 1 deletion compiler/K_nothrow_wrappers.fx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
the same variable can be saved non-nothrow functions with same signature.
Example:

@pure @nothrow fun foo(i: int): int = @ccode {...}
@pure @nothrow fun foo(i: int): int @ccode {...}
fun bar(i: int): int {...}
val predicate = if (whistle_of_crayfish_on_a_mount) {foo} else {bar}
val denied_ips = [1, 2, 3, 4, 5, 6, 7, 8, ... ]
Expand Down
26 changes: 15 additions & 11 deletions compiler/Lexer.fx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type token_t =
| DOT_EQUAL | AUG_BINOP: Ast.binary_t | SPACESHIP | CMP: Ast.cmpop_t
| DOT_SPACESHIP | DOT_CMP: Ast.cmpop_t | SAME | RESERVED: string
| PP_IF | PP_IFDEF | PP_IFNDEF | PP_ELIF | PP_ELSE
| PP_ENDIF | PP_DEFINE | PP_UNDEF
| PP_ENDIF | PP_DEFINE | PP_UNDEF | PP_ERROR | PP_WARNING

fun ne2u(ne: bool, s: string) = if ne {s} else {s.decapitalize()}

Expand Down Expand Up @@ -148,6 +148,8 @@ fun tok2str(t: token_t)
| PP_ENDIF => ("PP_ENDIF", "@ENDIF")
| PP_DEFINE => ("PP_DEFINE", "@DEFINE")
| PP_UNDEF => ("PP_UNDEF", "@UNDEF")
| PP_ERROR => ("PP_ERROR", "@ERROR")
| PP_WARNING => ("PP_WARNING", "@WARNING")
}

fun getnumber(s: string, pos: int, loc: lloc_t, just_int: bool): (int, token_t) =
Expand Down Expand Up @@ -216,7 +218,8 @@ var ficus_keywords = Hashmap.from_list("", (FUN, 0),
("@pure", (PURE, 2)), ("@unzip", (UNZIP, 2)),
("@IF", (PP_IF, 2)), ("@IFDEF", (PP_IFDEF, 2)), ("@IFNDEF", (PP_IFNDEF, 2)),
("@ELIF", (PP_ELIF, 1)), ("@ELSE", (PP_ELSE, 1)), ("@ENDIF", (PP_ENDIF, 3)),
("@DEFINE", (PP_DEFINE, 2)), ("@UNDEF", (PP_UNDEF, 2))
("@DEFINE", (PP_DEFINE, 2)), ("@UNDEF", (PP_UNDEF, 2)),
("@ERROR", (PP_ERROR, 2)), ("@WARNING", (PP_WARNING, 2))
])

/* The function that returns the actual tokenizer/lexer function,
Expand Down Expand Up @@ -405,10 +408,7 @@ fun make_lexer(strm: stream_t): (void -> (token_t, lloc_t) list)
} else {
(LITERAL(Ast.LitString(res)), loc) :: []
}
} else if c.isalpha() || c == '_' || (c == '@' && c1.isalpha() &&
(new_exp ||
(c1 == 's' && buf.zero[pos+2] == 'y' &&
buf.zero[pos+3] == 'n' && buf.zero[pos+4] == 'c'))) {
} else if c.isalpha() || c == '_' || (c == '@' && c1.isalpha()) {
var p = pos+1
while p < len {
val cp = buf[p]
Expand All @@ -417,10 +417,10 @@ fun make_lexer(strm: stream_t): (void -> (token_t, lloc_t) list)
}
val ident = buf[pos:p].copy()
pos = p
val t =
prev_dot = false
match ficus_keywords.find_opt(ident) {
| Some((t, n)) =>
match (t, n) {
val t = match (t, n) {
| (CCODE, _) =>
check_ne(new_exp, loc, "ccode")
paren_stack = (CCODE, loc) :: paren_stack
Expand Down Expand Up @@ -463,11 +463,15 @@ fun make_lexer(strm: stream_t): (void -> (token_t, lloc_t) list)
| (t, 2) => check_ne(new_exp, loc, ident); new_exp = true; t
| _ => throw Lxu.LexerError(loc, f"Unexpected keyword '{ident}'")
}
(t, loc) :: []
| _ =>
val t = IDENT(new_exp, ident); new_exp = false; t
if c == '@' {
val t = IDENT(new_exp, ident[1:]); new_exp = false
(AT, loc) :: (t, getloc(pos+1)) :: []
} else {
val t = IDENT(new_exp, ident); new_exp = false; (t, loc) :: []
}
}
prev_dot = false
(t, loc) :: []
} else {
val prev_ne = new_exp
prev_dot = false
Expand Down
52 changes: 52 additions & 0 deletions compiler/Options.fx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
import Filename, Sys

type optval_t = OptBool: bool | OptInt: int | OptString: string

type options_t =
{
app_args: string list = [];
Expand All @@ -19,6 +21,7 @@ type options_t =
gen_c: bool = true;
include_path: string list = [];
debug: bool = false;
defines: (string, optval_t) list = [];
optim_iters: int = 0;
inline_thresh: int = 100;
enable_openmp: bool = true;
Expand Down Expand Up @@ -86,6 +89,15 @@ where options can be some of:
-Wno-unused Do not report errors about unused values/functions
-o <output_name> Output file name (by default it matches the
input filename without .fx extension)
-D symbol Define 'symbol=true' for preprocessor
-D symbol=value Define 'symbol=value' for preprocessor.
The value may be one of the following:
* a boolean: true (or on), false (or off),
* an integer (if the value starts with a digit, '+' or '-' and
contains 1 or more decimal digits)
* text string: everything between double quotes or
any combination of characters that does not
contain spaces, '\' or quotes and does not start with a digit or '-'
-I <dir> Add specified directory to the module search path
-B <build_root> Specifies the parent directory <build_root> where subdirectory
<build_root>/__fxbuild__/<app_build_dir> with the generated files will be created.
Expand Down Expand Up @@ -174,6 +186,46 @@ fun parse_options(): bool {
opt.verbose = true; next
| "-o" :: oname :: next =>
opt.output_name = oname; next
| "-D" :: nameval :: next =>
val p1 = nameval.find('=')
val (name, value) = if p1 < 0 {(nameval, "true")}
else {(nameval[:p1], nameval[p1+1:])}
val errval = OptBool(false)
val value =
if name != "" && (name[0].isalpha() || name[0]=='_') &&
all(for c <- name {c.isalnum() || c == '_'}) {
if value == "TRUE" || value == "true" || value == "ON" || value == "on" {OptBool(true)}
else if value == "FALSE" || value == "false" || value == "OFF" || value == "off" {OptBool(false)}
else if value == "" {
println(f"a value should follow after '{name}='")
ok = false
errval
} else if value[0] == '+' || value[0] == '-' || value[0].isdigit() {
match value.to_int() {
| Some(x) => OptInt(x)
| _ =>
println(f"invalid numerical value '{value}' of a symbol '{name}'; \
if you meant a string, enclose it in double quotes")
ok = false
errval
}
} else if value.startswith('\"') {
if !value.endswith('\"') {
println(f"the value {value} starts with '\"', but does not terminate with '\"'")
ok = false
errval
} else {
OptString(value[1:-1])
}
} else {
OptString(value)
}
} else {
println(f"identifier '{name}' contains incorrect characters")
ok = false; errval
}
if ok { opt.defines = (name, value) :: opt.defines; next }
else { [] }
| "-I" :: incdir :: next =>
opt.include_path = opt.include_path + (incdir :: []); next
| "-B" :: bdir :: next =>
Expand Down
32 changes: 26 additions & 6 deletions compiler/Parser.fx
Original file line number Diff line number Diff line change
Expand Up @@ -2008,8 +2008,7 @@ fun preprocess(ts: tklist_t): tklist_t
{
var env = Hashmap.empty(256, "", PP_INT(0L))

fun pp_err(ts: tklist_t, msg: string) =
parse_err(ts, "preprocessor: " + msg)
fun pp_err(ts: tklist_t, msg: string) = parse_err(ts, msg)

fun pp_match_paren((ts: tklist_t, x: ppval_t), ct: token_t, ol: loc_t): (tklist_t, ppval_t)
{
Expand All @@ -2021,8 +2020,8 @@ fun preprocess(ts: tklist_t): tklist_t

fun pp_atomic(ts: tklist_t, calc: bool): (tklist_t, ppval_t)
{
//println(f"pp_atomic @ {ts.hd().1}, calc={calc}\n")
val defval = PP_BOOL(false)
//println(f"pp_atomic @ {ts.hd().1}\n")
match ts {
| (IDENT(_, "DEFINED"), _) :: (LPAREN(false), _) :: (IDENT(_, n), _) :: (RPAREN, _) :: rest =>
(rest, if calc {PP_BOOL(env.mem(n))} else {defval})
Expand All @@ -2038,12 +2037,12 @@ fun preprocess(ts: tklist_t): tklist_t
| _ => throw pp_err(ts, f"unknown/unsupported function {fname}")
}
} else {defval}
(rest, x)
(ts, x)
| (IDENT(ne, i), _) :: rest =>
val x = if calc {
match env.find_opt(i) {
| Some(x) => x
| _ => throw pp_err(ts, f"identifier 'i' is undefined")
| _ => throw pp_err(ts, f"identifier '{i}' is undefined")
}
} else {
defval
Expand Down Expand Up @@ -2273,6 +2272,7 @@ fun preprocess(ts: tklist_t): tklist_t

fun pp_exp(ts: tklist_t, calc: bool): (tklist_t, ppval_t)
{
//println(f"pp_exp @ {ts.hd().1}, calc={calc}\n")
val (ts, x) = pp_chained_cmp(ts, calc)
pp_logic(ts, calc, x, 0)
}
Expand All @@ -2297,7 +2297,7 @@ fun preprocess(ts: tklist_t): tklist_t
| (PP_DEFINE, _) :: rest =>
match rest {
| (IDENT(_, n), _) :: rest =>
val (ts, x) = pp_exp(rest, true)
val (ts, x) = pp_exp(rest, process)
if process {
if env.mem(n) {throw pp_err(ts, f"symbol '{n}' is already defined")}
env.add(n, x)
Expand Down Expand Up @@ -2364,6 +2364,26 @@ fun preprocess(ts: tklist_t): tklist_t
| _ => {}
}
ppnext(ts.tl(), ppstack.tl(), result)
| (PP_ERROR, _) :: _ =>
val (next_ts, x) = pp_exp(ts.tl(), process)
if process {
val msg = match x {
| PP_STRING(x) => x
| _ => "@ERROR argument must be a string"
}
throw pp_err(ts, msg)
}
ppnext(next_ts, ppstack, result)
| (PP_WARNING, _) :: _ =>
val (next_ts, x) = pp_exp(ts.tl(), process)
if process {
val msg = match x {
| PP_STRING(x) => x
| _ => throw pp_err(ts, "@WARNING argument must be a string")
}
println(f"{ts.hd().1}: warning: {msg}")
}
ppnext(next_ts, ppstack, result)
| (AT, _) :: (LBRACE, l1) :: rest =>
val (ts, x) = pp_match_paren(pp_exp(rest, process), RBRACE, l1)
val result = if process {
Expand Down
Loading

0 comments on commit e9d06fe

Please sign in to comment.