diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a8eb576b..244f62cd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,7 @@ add_executable(c3c src/compiler/bigint.c src/compiler/bigint.h src/compiler/context.c - src/compiler/expr_analysis.c + src/compiler/sema_expr.c src/compiler/enums.h src/compiler/casts.c src/compiler/target.c @@ -84,7 +84,7 @@ add_executable(c3c src/compiler/llvm_codegen_type.c src/compiler/llvm_codegen_function.c src/build/builder.c - src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c src/target_info/target_info.c) + src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c src/target_info/target_info.c src/compiler/parse_expr.c src/compiler/parser_internal.h src/compiler/parse_stmt.c src/compiler/sema_passes.c src/compiler/sema_internal.h src/compiler/sema_decls.c src/compiler/sema_types.c src/compiler/sema_stmts.c) target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter) diff --git a/resources/examples/acornvm/parser.c3 b/resources/examples/acornvm/parser.c3 index b0c25447d..85c48f93c 100644 --- a/resources/examples/acornvm/parser.c3 +++ b/resources/examples/acornvm/parser.c3 @@ -90,7 +90,8 @@ func int CompInfo.findClosureVar(CompInfo *comp, Value varnm) } /** If variable not declared already, declare it */ -void declareLocal(CompInfo *comp, Value varnm) { +void CompInfo.declareLocal(CompInfo *comp, Value varnm) +{ Value th = comp->th; // If explicit 'local' declaration, declare if not found in block list if (comp->forcelocal) { @@ -103,7 +104,7 @@ void declareLocal(CompInfo *comp, Value varnm) { if (comp->prevcomp!=aNull && findLocalVar((CompInfo*)comp->prevcomp, varnm)!=-1) { arrAdd(th, comp->clovarseg, varnm); // Add initialization logic - astAddSeg2(th, comp->newcloseg, vmlit(SymLocal), varnm); // Add its initialization to new closure segment + astAddSeg2(th, comp->newcloseg, vmlit(SYM_LOCAL), varnm); // Add its initialization to new closure segment } else arrAdd(th, comp->locvarseg, varnm); @@ -115,15 +116,15 @@ func Value parseNewClo(CompInfo* comp, Value astseg) { Value th = comp->th; // ('Closure', clovars, ('callprop', Closure, New, getmethod, setmethod)) - Value closeg = ast::addSeg(th, astseg, vmlit(SymClosure), 3); + Value closeg = ast::addSeg(th, astseg, vmlit(SYM_CLOSURE), 3); comp->clovarseg = pushArray(th, aNull, 4); arr::add(th, closeg, comp->clovarseg); th.popValue(); - Value newcloseg = ast::addSeg(th, closeg, vmlit(SymCallProp), 8); - ast::addSeg2(th, newcloseg, vmlit(SymGlobal), vmlit(SymClosure)); - ast::addSeg2(th, newcloseg, vmlit(SymLit), vmlit(SymNew)); - ast::addSeg2(th, newcloseg, vmlit(SymLit), vmlit(SymNull)); - ast::addSeg2(th, newcloseg, vmlit(SymLit), vmlit(SymNull)); + Value newcloseg = ast::addSeg(th, closeg, vmlit(SYM_CALL_PROP), 8); + ast::addSeg2(th, newcloseg, vmlit(SYM_GLOBAL), vmlit(SYM_CLOSURE)); + ast::addSeg2(th, newcloseg, vmlit(SYM_LIT), vmlit(SYM_NEW)); + ast::addSeg2(th, newcloseg, vmlit(SYM_LIT), vmlit(SYM_NULL)); + ast::addSeg2(th, newcloseg, vmlit(SYM_LIT), vmlit(SYM_NULL)); return newcloseg; } @@ -134,12 +135,12 @@ void parseValue(CompInfo* comp, Value astseg) // Literal token (number, symbol, string, true, false, null) if (comp->lex->toktype == Lit_Token) { - ast::addSeg2(th, astseg, vmlit(SymLit), comp.lex.token); + ast::addSeg2(th, astseg, vmlit(SYM_LIT), comp.lex.token); lexGetNextToken(comp->lex); } // Static unquoted @url else if (comp->lex->toktype == Url_Token) { - astAddSeg2(th, astseg, vmlit(SymExt), anInt(genAddUrlLit(comp, comp->lex->token))); + astAddSeg2(th, astseg, vmlit(SYM_EXT), anInt(genAddUrlLit(comp, comp->lex->token))); lexGetNextToken(comp->lex); } // Local or global variable / name token @@ -151,33 +152,36 @@ void parseValue(CompInfo* comp, Value astseg) if (lexMatch(comp->lex, ":") || lexMatch(comp->lex, ":=")) astAddSeg2(th, astseg, vmlit(SymLit), symnm); else if (first=='$' || (first>='A' && first<='Z')) - astAddSeg2(th, astseg, vmlit(SymGlobal), symnm); - else { - declareLocal(comp, symnm); // declare local if not already declared + { + astAddSeg2(th, astseg, vmlit(SYM_GLOBAL), symnm); + } + else + { + comp.declareLocal(symnm); // declare local if not already declared // We do not resolve locals to index until gen because of control clauses (declaration after use) - astAddSeg2(th, astseg, vmlit(SymLocal), symnm); + astAddSeg2(th, astseg, vmlit(SYM_LOCAL), symnm); } popValue(th); } // 'baseurl' pseudo-variable else if (lexMatchNext(comp->lex, "baseurl")) { - astAddValue(th, astseg, vmlit(SymBaseurl)); + astAddValue(th, astseg, vmlit(SYM_BASEURL)); } // 'this' pseudo-variable else if (lexMatchNext(comp->lex, "this")) { - astAddValue(th, astseg, vmlit(SymThis)); + astAddValue(th, astseg, vmlit(SYM_THIS)); } // 'self' pseudo-variable else if (lexMatchNext(comp->lex, "self")) { - astAddValue(th, astseg, vmlit(SymSelf)); + astAddValue(th, astseg, vmlit(SYM_SELF)); } // 'selfmethod' pseudo-variable else if (lexMatchNext(comp->lex, "selfmethod")) { - astAddValue(th, astseg, vmlit(SymSelfMeth)); + astAddValue(th, astseg, vmlit(SYM_SELF_METH)); } // 'context' pseudo-variable else if (lexMatchNext(comp->lex, "context")) { - astAddValue(th, astseg, vmlit(SymContext)); + astAddValue(th, astseg, vmlit(SYM_CONTEXT)); } // '...' splat else if (lexMatchNext(comp->lex, "...")) { @@ -285,7 +289,8 @@ void parseValue(CompInfo* comp, Value astseg) } /** Add a list of parameters to a AST propseg */ -void parseParams(CompInfo* comp, Value propseg, const char *closeparen) { +func void CompInfo.parseParams(CompInfo* comp, Value propseg, const char *closeparen) +{ bool saveforcelocal = comp->forcelocal; comp->forcelocal = false; @@ -330,7 +335,7 @@ void parseTerm(CompInfo* comp, Value astseg) { else if (lexMatchNext(comp->lex, "[")) { astSetValue(th, propseg, 0, vmlit(SymCallProp)); // adjust because of parms astAddSeg2(th, propseg, vmlit(SymLit), vmlit(SymBrackets)); - parseParams(comp, propseg, "]"); + comp.parseParams(propseg, "]"); getparms = false; } // Handle '.', '.:', and '::' @@ -367,7 +372,7 @@ void parseTerm(CompInfo* comp, Value astseg) { if (getparms) { if (lexMatchNext(comp->lex, "(")) { astSetValue(th, propseg, 0, vmlit(SymCallProp)); // adjust because of parms - parseParams(comp, propseg, ")"); + comp.parseParams(propseg, ")"); } // Treat Text or Symbol literal as a single parameter to pass else if (comp->lex->toktype == Lit_Token && (isStr(comp->lex->token) || isSym(comp->lex->token))) { @@ -394,7 +399,7 @@ void parsePrefixExp(CompInfo* comp, Value astseg) { else { // Not a literal number? Do the property call astseg = astInsSeg(th, astseg, vmlit(SymCallProp), 3); Value litseg = astAddSeg(th, astseg, vmlit(SymLit), 2); - astAddValue(th, litseg, vmlit(SymNeg)); + astAddValue(th, litseg, vmlit(SYM_NEG)); } } // '@' + symbol, text or '('exp')' diff --git a/resources/examples/acornvm/value.c3 b/resources/examples/acornvm/value.c3 index 6de2ecfed..8a971e746 100644 --- a/resources/examples/acornvm/value.c3 +++ b/resources/examples/acornvm/value.c3 @@ -3,7 +3,7 @@ module acornvm::value; /** A convenience macro for assert(), establishing the conditions expected to be true, * before returning expression e */ -macro @assert_exp($c, auto $e) +macro assert_exp($c, $e) { assert($c); return $e; @@ -54,7 +54,7 @@ typedef func int(Value) as AcMethodp; * Great for null, false, true, integers and symbols. * Less suitable for floats (no epsilon) and comparing contents of containers (e.g., strings). * Is fast because it avoids using type-specific methods. */ -macro @isSame(a, b) { return (a == b); } +macro isSame(a, b) { return (a == b); } /** What type of data is encoded within the value, as established by the last 2 bits. * Because of 32/64-bit allocation alignment, pointers always have 0 in last 2 bits. @@ -84,9 +84,9 @@ bool Value.isStr(Value *str) return str.isEnc(StrEnc) && !(str_info(str)->flags1 & StrCData); } -macro @isType(v, ValBits e) +macro isType(v, ValBits e) { - return @cast(v, Auint) & VAL_MASK == e; + return cast(v, Auint) & VAL_MASK == e; } // Integer value functions @@ -101,16 +101,16 @@ func bool Value.isInt(Value *v) /** Cast c-integer n into an Integer value * This loses top two-bits of integer precision. * If integer is too large, this could result in an unexpected value and change of sign. */ -macro @anInt(n) +macro anInt(n) { - return @cast(@cast(n, Aint) << VAL_SHIFT + ValInt, Value); + return cast(cast(n, Aint) << VAL_SHIFT + ValInt, Value); } /** Cast an Integer value into a c-integer * Note: It assumes (and won't verify) that v is an Integer */ -macro @toAint(v) +macro toAint(v) { - return @cast(v, Aint) >> VAL_SHIFT; + return cast(v, Aint) >> VAL_SHIFT; } // Float value functions @@ -135,19 +135,19 @@ AVM_API Afloat toAfloat(Value v); **************************************************** */ /** The null value */ -macro @aNull() +macro aNull() { return @cast(0 << VAL_SHIFT, ValCons, Value); } /** The false value */ -macro @aFalse() +macro aFalse() { return @cast(1 << VAL_SHIFT + ValCons, Value); } /** The true value */ -macro @aTrue() +macro aTrue() { return @cast(2 << VAL_SHIFT + ValCons, Value); } diff --git a/resources/examples/acornvm/vm.c3 b/resources/examples/acornvm/vm.c3 index 20e4fc15a..2c5462632 100644 --- a/resources/examples/acornvm/vm.c3 +++ b/resources/examples/acornvm/vm.c3 @@ -96,100 +96,100 @@ const N_STD_SYM = 256; /** C index values for all VM literal values used throughout the code for common symbols and core types. They are forever immune from garbage collection by being anchored to the VM. */ -enum VmLiterals +enum VmLiteral : int (string name = nil) { // Compiler symbols - SYM_NULL, //!< 'null' - SYM_FALSE, //!< 'false' - SYM_TRUE, //!< 'true' - SYM_AND, //!< 'and' - SymAsync, //!< 'async' - SymBaseurl, //!< 'baseurl' - SymBreak, //!< 'break' - SymContext, //!< 'context' - SymContinue, //!< 'continue' - SymDo, //!< 'do' - SymEach, //!< 'each' - SymElse, //!< 'else' - SymElif, //!< 'elif' - SymIf, //!< 'if' - SymIn, //!< 'in' - SymInto, //!< 'into' - SymMatch, //!< 'match' - SymNot, //!< 'not' - SymOr, //!< 'or' - SymReturn, //!< 'return' - SymSelf, //!< 'self' - SymSelfMeth, //!< 'selfmethod' - SymThis, //!< 'this' - SymUsing, //!< 'using' + SYM_NULL("null"), + SYM_FALSE("false"), + SYM_TRUE("true"), + SYM_AND("and"), + SYM_ASYNC("async"), + SYM_BASE_URL("baseurl"), + SYM_BREAK("break"), + SYM_CONTEXT("context"), + SYM_CONTINUE("continue"), + SYM_DO("do"), + SYM_EACH("each"), + SYM_ELSE("else"), + SYM_ELIF("elif"), + SYM_IF("if"), + SYM_IN("in", + SYM_INTO("into"), + SYM_MATCH("match"), + SYM_NOT("not"), + SYM_OR("or"), + SYM_RETURN("return"), + SYM_SELF("self", + SYM_SELF_METH("selfmethod"), + SYM_THIS("this"), + SYM_USING("using"), SymVar, //!< 'var' - SymWait, //!< 'wait' - SymWhile, //!< 'while' - SymWith, //!< 'with' - SymYield, //!< 'yield' - SymLBrace, //!< '{' - SymRBrace, //!< '}' - SymSemicolon, //!< ';' - SymComma, //!< ',' - SymQuestion, //!< '?' - SymAt, //!< '@' - SymSplat, //!< '...' - SymDot, //!< '.' - SymColons, //!< '::' - SymDotColon, //!< '.:' + SYM_WAIT("wait"), + SYM_WHILE("while"), + SYM_WITH("with"), + SYM_YIELD("yield", - // Compiler symbols that are also methods - SymAppend, //!< '<<' - SymPrepend, //!< '>>' - SymPlus, //!< '+' - SymMinus, //!< '-' - SymMult, //!< '*' - SymDiv, //!< '/' - SymRocket, //!< '<=>' - SymEquiv, //!< '===' - SymMatchOp, //!< '~~' - SymLt, //!< '<' - SymLe, //!< '<=' - SymGt, //!< '>' - SymGe, //!< '>=' - SymEq, //!< '==' - SymNe, //!< '!=' + SYM_LBRACE("{"), + SYM_RBRACE("}"), + SYM_SEMICOLON(";"), + SYM_COMMA(","), + SYM_QUESTION("?"), + SYM_AT("@"), + SYM_SPLAT("..."), + SYM_DOT("."), + SYM_COLONS("::"), + SYM_DOT_COLON(".:"), - // Methods that are not compiler symbols - // Byte-code (and parser) standard methods - SYM_NEW, //!< 'New' - SYM_INIT, //!< 'Init' - SYM_LOAD, //!< 'Load' - SymGet, //!< 'Get' - SymParas, //!< '()' - SymBrackets, //!< '[]' - SymNeg, //!< '-@' - SymValue, //!< 'value' - SymEachMeth, //!< 'Each' - SymBegin, //!< 'Begin' - SymEnd, //!< 'End' + // Compiler symbols that are also methods + SYM_APPEND("<<"), + SYM_PREPENT(">>"), + SYM_PLUS("+"), + SYM_MINUS("-"), + SYM_MULT("*"), + SYM_DIV("/"), + SYM_ROCKET("<=>"), + SYM_EQUIV("==="), + SYM_MATCHOP("~~"), + SYM_LT("<"), + SYM_LE("<="), + SYM_GT(">"), + SYM_GE(">="), + SYM_EQ("=="), + SYM_NE("!="), - SymFinalizer, //!< '_finalizer' method for CData - SymName, //!< '_type' symbol + // Methods that are not compiler symbols + // Byte-code (and parser) standard methods + SYM_NEW("New"), + SYM_INIT("Init"), + SYM_LOAD("Load"), + SYM_GET("Get"), + SYM_PARAS("()"), + SYM_BRACKETS("[]"), + SYM_NEG("-@"), + SYM_VALUE("value"), + SYM_EACH_METH("Each"), + SYM_BEGIN("Begin"), + SYM_END("End"), - // AST symbols - SymMethod, //!< 'method' - SymAssgn, //!< '=' - SymOrAssgn, //!< '||=' - SymColon, //!< ':' - SymThisBlock, //!< 'thisblock' - SymCallProp, //!< 'callprop' - SymActProp, //!< 'activeprop' - SymRawProp, //!< 'rawprop' - SymGlobal, //!< 'global' - SymLocal, //!< 'local' - SymLit, //!< 'lit' - SymExt, //!< 'ext' - SymRange, //!< 'Range' - SymClosure, //!< 'Closure' - SymYielder, //!< 'Yielder' - SymResource, //!< 'Resource' + SYM_FINALIZER("_finalizer") // method for CData + SYM_NAME('_type') // symbol + + // AST symbols + SYM_METHOD("method"), + SYM_ASSGN("="), + SYM_OR_ASSGN("||="), + SYM_COLON(":"), + SYM_THIS_BLOCK("thisblock"), + SYM_CALL_PROP("callprop"), + SYM_ACT_PROP("activeprop"), + SYM_RAW_PROP("rawprop"), + SYM_LOCAL("local"), + SYM_LIT("lit"), + SYM_EXT("ext"), + SYM_RANGE("Range"), + SYM_CLOSURE("Closure"), + SYM_YIELDER("Yielder"), + SYM_RESOURCE("Resource"), // Core type type TypeObject, //!< Type @@ -229,11 +229,15 @@ enum VmLiterals nVmLits } -macro @vmlit!(lit) { return arr_inf(vm(th)->literals)->arr[list]; } +macro vmlit!(VmLiteral lit) +{ + return arr_inf(vm(th)->literals)->arr[list]; +} + /** Used by vm_init to build random seed */ -macro @memcpy_Auint(i, val) +macro memcpy_Auint(i, val) { Auint anint = @cast(val, Auint); memcpy(seedstr + i * sizeof(Auint), &anint, sizeof(Auint)); @@ -397,95 +401,8 @@ struct vmLitSymEntry /** Constant array that identifies and maps all VM literal symbols */ vmLitSymEntry[+] vmLitSymTable = { // Compiler reserved names - {SymNull, "null"}, - {SymFalse, "false"}, - {SymTrue, "true"}, - {SymAnd, "and"}, - {SymAsync, "async"}, - {SymBaseurl, "baseurl"}, - {SymBreak, "break"}, - {SymContext, "context"}, - {SymContinue, "continue"}, - {SymDo, "do"}, - {SymEach, "each"}, - {SymElse, "else"}, - {SymElif, "elif"}, - {SymIf, "if"}, - {SymIn, "in"}, - {SymInto, "into"}, - {SymLocal, "local"}, - {SymMatch, "match"}, - {SymNot, "not"}, - {SymOr, "or"}, - {SymReturn, "return"}, - {SymSelf, "self"}, - {SymSelfMeth, "selfmethod"}, - {SymThis, "this"}, - {SymUsing, "using"}, - {SymWait, "wait"}, - {SymWhile, "while"}, - {SymWith, "with"}, - {SymYield, "yield"}, - {SymLBrace, "{"}, - {SymRBrace, "}"}, - {SymSemicolon, ";"}, - {SymComma, ","}, - {SymQuestion, "?"}, - {SymAt, "@"}, - {SymSplat, "..."}, - {SymDot, "."}, - {SymColons, "::"}, - {SymDotColon, ".:"}, - // Compiler symbols that are also methods - {SymAppend, "<<"}, - {SymPrepend, ">>"}, - {SymPlus, "+"}, - {SymMinus, "-"}, - {SymMult, "*"}, - {SymDiv, "/"}, - {SymRocket, "<=>"}, - {SymEquiv, "==="}, - {SymMatchOp, "~~"}, - {SymLt, "<"}, - {SymLe, "<="}, - {SymGt, ">"}, - {SymGe, ">="}, - {SymEq, "=="}, - {SymNe, "!="}, - // Methods that are not compiler symbols - {SymNew, "New"}, - {SymInit, "Init"}, - {SymLoad, "Load"}, - {SymGet, "Get"}, - {SymParas, "()"}, - {SymBrackets, "[]"}, - {SymNeg, "-@"}, - {SymValue, "value"}, - {SymEachMeth, "Each"}, - {SymBegin, "Begin"}, - {SymEnd, "End"}, - - {SymFinalizer, "_finalizer"}, - {SymName, "_name"}, - - // AST symbols - {SymMethod, "method"}, - {SymAssgn, "="}, - {SymOrAssgn, "||="}, - {SymColon, ":"}, - {SymThisBlock, "thisblock"}, - {SymCallProp, "callprop"}, - {SymActProp, "activeprop"}, - {SymRawProp, "rawprop"}, - {SymGlobal, "global"}, - {SymLit, "lit"}, - {SymExt, "ext"}, - {SymRange, "Range"}, - {SymClosure, "Closure"}, - {SymYielder, "Yielder"}, - {SymResource, "Resource"}, // End of literal table {0, NULL} @@ -504,23 +421,23 @@ void vm_litinit(Value th) { // Load up literal symbols from table const struct vmLitSymEntry *vmlittblp = &vmLitSymTable[0]; - while (vmlittblp->symnm) { - newSym(th, &vmlits[vmlittblp->litindex], vmlittblp->symnm, strlen(vmlittblp->symnm)); - vmlittblp++; + for (VmLiteral i = 0; i <= SYM_RESOUCE; i++) + { + newSym(th, &vmlits[i], i.name); } } /** Map byte-code's standard symbols to VM's literals (max. number at 256) */ const int stdTblMap[] = { // Commonly-called methods - SymNew, // 'new' - SymParas, // '()' - SymAppend, // '<<' - SymPlus, // '+' - SymMinus, // '-' - SymMult, // '*' - SymDiv, // '/' - SymNeg, // '-@' + SYM_NEW, // 'new' + SYM_PARAS, // '()' + SYM_APPEND, // '<<' + SYM_PLUS, // '+' + SYM_MINUS, // '-' + SYM_MULT, // '*' + SYM_DIV, // '/' + SYM_NET, // '-@' -1 }; diff --git a/resources/examples/http.c3 b/resources/examples/http.c3 index dab7b3e11..4f5e96360 100644 --- a/resources/examples/http.c3 +++ b/resources/examples/http.c3 @@ -2,7 +2,9 @@ import curl; func int main(void) { - try Curl curl = curl::newCurl(); + Curl curl; + + try curl.init(); catch (error e) { diff --git a/resources/examples/madlibs.c3 b/resources/examples/madlibs.c3 index 7cdf9322d..63ce93359 100644 --- a/resources/examples/madlibs.c3 +++ b/resources/examples/madlibs.c3 @@ -4,7 +4,7 @@ import regex, stdio; func void main() { println("Enter a story template, terminated by an empty line:"); - string@ story = ""; + string story = ""; while (1) { try string line = stdin.readln().strip(); @@ -12,7 +12,10 @@ func void main() story += line + "\n"; } - Regex* r = try regex::regexWithOptions("<.+?>", RegexOpt.GLOBAL) catch @unreachable; + Regex r; + + r.initWithOptions("<.+?>", RegexOpt.GLOBAL) else @unreachable; + defer r.destroy(); foreach (RegexMatch* match : r.match(story)) { diff --git a/resources/examples/map.c3 b/resources/examples/map.c3 index 8f9c14248..e112b0313 100644 --- a/resources/examples/map.c3 +++ b/resources/examples/map.c3 @@ -3,9 +3,9 @@ module map(Key, Type); public struct Entry { Key key; - Type *value; + Type* value; usize hash; - Entry *next; + Entry* next; } public struct Map @@ -92,10 +92,10 @@ public macro Vector.foreach(Vector *vector, macro void(Type value) body) test { define IntVector = Vector(int); - IntVector *vector = @calloc(IntVector); + IntVector vector = vector.init(); vector.add(1); vector.add(2); - foreach (int i : vector) + for (int i : vector) { printDigit(i); } @@ -103,4 +103,5 @@ test { printDigit(i); } + vector.destroy(); } \ No newline at end of file diff --git a/resources/examples/vector.c3 b/resources/examples/vector.c3 index 44b6e9e71..4e8eadc4b 100644 --- a/resources/examples/vector.c3 +++ b/resources/examples/vector.c3 @@ -5,9 +5,9 @@ public struct Vector Type[*] array; } -public func Vector@ newVector() +public func void Vector.init() { - return @calloc(Vector); + array = nil; } public func void Vector.add(Vector *vector, Type type) diff --git a/resources/examples/window.c3 b/resources/examples/window.c3 index 6b3322379..e1ef9361f 100644 --- a/resources/examples/window.c3 +++ b/resources/examples/window.c3 @@ -5,7 +5,7 @@ uint counter = 0; func void clickedme(GtkButton *o, void *d) { - cast(GtkLabel*, d).set_text(string.format("You clicked me %d times", ++counter)); + cast(d, GtkLabel*).set_text(string.format("You clicked me %d times", ++counter)); } int main(int argc, string[] argv) diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 7f72a8926..a84f2a372 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -228,11 +228,40 @@ func int jumpback(int x) x += 3; } } + if (x < 100) goto LABELX; return x + 1; } + +func void test_expr_block(int x) +{ + int a = ({ + if (x > 0) return x * 2; + if (x == 0) return 100; + return -x; + }); + //printf("The result was %d\n", a); +} + +func int expr_block() +{ + int fok = ({ return ({ return 10; }); }); + int y = 2; + int x = ({ + if (y < 10) return 10; + return 2; + }); + + /* ({ + return 3; + });*/ + return x; + +} func int xxxx(int x) { + + { x += 10; defer printf("XXX"); @@ -306,7 +335,7 @@ func int main(int x) int ef = 3; int *eff = &ef; eff[0] = 4; - byte *ex = cast(byte *, eff); + byte *ex = cast(eff, byte*); ex[0] = 5; if (eff[0] == 5) printf("Works-5!\n"); ex[1] = 5; @@ -321,7 +350,8 @@ func int main(int x) bool eok = true; long deee = (eok ? a : b) + (eok ? b : c);*/ int i = 0; -JUMP: + + JUMP: i = i + 1; //@hello(); printf("Hello worldABC" "D" "E\u2701\n"); @@ -352,7 +382,7 @@ JUMP: func void test2(int* x, int y, int z) { *(&(&x)[0]); - float cheat = cast(int, x); + float cheat = cast(x, int); x++; z = 0; diff --git a/src/compiler/ast.c b/src/compiler/ast.c index db9b2fd27..6aba30d4d 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -4,6 +4,8 @@ #include "compiler_internal.h" +static void fprint_asts_recursive(FILE *file, Ast **asts, int indent); + Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility) { assert(name.string); @@ -515,6 +517,17 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprintf_indented(file, indent, "(ident %s\n", expr->identifier_expr.identifier); fprint_expr_common(file, expr, indent + 1); break; + case EXPR_EXPR_BLOCK: + if (!expr->expr_block.stmts) + { + fprintf(file, "(expr_block)\n"); + return; + } + fprintf(file, "(expr_block\n"); + { + fprint_asts_recursive(file, expr->expr_block.stmts, indent + 1); + } + break; case EXPR_CONST: fprintf_indented(file, indent, "(const "); switch (expr->const_expr.type) @@ -858,17 +871,6 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) fprint_indent(file, indent); switch (ast->ast_kind) { - case AST_FUNCTION_BLOCK_STMT: - if (!ast->compound_stmt.stmts) - { - fprintf(file, "(function_block)\n"); - return; - } - fprintf(file, "(function_block\n"); - { - fprint_asts_recursive(file, ast->compound_stmt.stmts, indent + 1); - } - break; case AST_COMPOUND_STMT: if (!ast->compound_stmt.stmts) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 6efefaed0..4561ebcc0 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -81,15 +81,20 @@ void compiler_compile(BuildTarget *target) { sema_analysis_pass_process_imports(contexts[i]); } + if (diagnostics.errors > 0) exit(EXIT_FAILURE); + VECEACH(contexts, i) { sema_analysis_pass_conditional_compilation(contexts[i]); } + if (diagnostics.errors > 0) exit(EXIT_FAILURE); + VECEACH(contexts, i) { sema_analysis_pass_decls(contexts[i]); } if (diagnostics.errors > 0) exit(EXIT_FAILURE); + VECEACH(contexts, i) { Context *context = contexts[i]; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 99b6ad0ab..b97d8df4b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -219,6 +219,7 @@ typedef struct { Decl *parent; Expr *expr; + Expr **args; uint64_t ordinal; } EnumConstantDecl; @@ -231,6 +232,7 @@ typedef struct typedef struct { Decl** values; + Decl** parameters; TypeInfo *type_info; } EnumDecl; @@ -473,6 +475,11 @@ typedef struct DeferList defers; } ExprScope; +typedef struct +{ + Ast **stmts; +} ExprFuncBlock; + struct _Expr { ExprKind expr_kind : 8; @@ -498,6 +505,7 @@ struct _Expr Expr** initializer_expr; Expr** expression_list; ExprScope expr_scope; + ExprFuncBlock expr_block; }; }; @@ -669,6 +677,25 @@ typedef struct DeferList defers; } AstThrowStmt; +typedef struct +{ + Token name; + Token constraints; + Token token; +} AsmOperand; + +typedef struct +{ + bool is_volatile : 1; + bool is_inline : 1; + bool is_goto : 1; + Expr *asm_template; + AsmOperand** output_operands; + AsmOperand** input_operands; + AsmOperand** clobbers; + Token* labels; +} AstAsmStmt; + typedef struct _Ast { SourceRange span; @@ -677,8 +704,8 @@ typedef struct _Ast union { AstAttribute attribute; + AstAsmStmt asm_stmt; AstCompoundStmt compound_stmt; - AstCompoundStmt function_block_stmt; Decl *declare_stmt; Expr *expr_stmt; AstThrowStmt throw_stmt; @@ -781,6 +808,13 @@ typedef struct _Context Token *trailing_comment; Token *next_lead_comment; DynamicScope *current_scope; + struct + { + Type *expected_block_type; + Ast **returns; + // Reusable returns cache. + Ast **returns_cache; + }; Decl *evaluating_macro; // Error handling struct @@ -882,6 +916,7 @@ static inline Ast *extend_ast_with_prev_token(Context *context, Ast *ast) } + void builtin_setup(Target *target); static inline bool builtin_may_negate(Type *canonical) @@ -957,6 +992,8 @@ bool context_set_module_from_filename(Context *context); bool context_set_module(Context *context, Path *path, Token *generic_parameters); void context_print_ast(Context *context, FILE *file); +#pragma mark --- Decl functions + Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility); Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility); Decl *decl_new_var(Token name, TypeInfo *type, VarDeclKind kind, Visibility visibility); @@ -973,6 +1010,8 @@ static inline DeclKind decl_from_token(TokenType type) UNREACHABLE } +#pragma mark --- Diag functions + void diag_reset(void); void diag_error_range(SourceRange span, const char *message, ...); void diag_verror_range(SourceRange span, const char *message, va_list args); @@ -1000,6 +1039,7 @@ static inline bool func_has_error_return(FunctionSignature *func_sig) return func_sig->throws || func_sig->throw_any; } +#pragma mark --- Lexer functions Token lexer_scan_token(Lexer *lexer); Token lexer_scan_ident_test(Lexer *lexer, const char *scan); @@ -1050,6 +1090,8 @@ void source_file_append_line_end(File *file, SourceLoc loc); SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc); SourcePosition source_file_find_position(SourceLoc loc); SourceRange source_range_from_ranges(SourceRange first, SourceRange last); +#define RANGE_EXTEND_PREV(x) (x)->span.end_loc = context->prev_tok_end + static inline unsigned source_range_len(SourceRange range) { return range.end_loc - range.loc; } void stable_init(STable *table, uint32_t initial_size); @@ -1193,4 +1235,10 @@ static inline const char* struct_union_name_from_token(TokenType type) #define DEBUG_TYPE(type) gencontext_get_debug_type(context, type) void advance(Context *context); -void advance_and_verify(Context *context, TokenType token_type); + +// Useful sanity check function. +static inline void advance_and_verify(Context *context, TokenType token_type) +{ + assert(context->tok.type == token_type); + advance(context); +} \ No newline at end of file diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 497c692e9..fcd572385 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -89,7 +89,6 @@ typedef enum AST_DO_STMT, AST_EXPR_STMT, AST_FOR_STMT, - AST_FUNCTION_BLOCK_STMT, AST_GENERIC_CASE_STMT, AST_GENERIC_DEFAULT_STMT, AST_GOTO_STMT, @@ -246,6 +245,7 @@ typedef enum EXPR_CAST, EXPR_SCOPED_EXPR, EXPR_MACRO_EXPR, + EXPR_EXPR_BLOCK, } ExprKind; @@ -281,6 +281,7 @@ typedef enum SCOPE_CONTINUE = 1 << 1, SCOPE_NEXT = 1 << 2, SCOPE_DEFER = 1 << 3, + SCOPE_EXPR_BLOCK = 1 << 4, } ScopeFlags; typedef enum diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index b84509da2..b5447218c 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -496,9 +496,9 @@ Token lexer_scan_token(Lexer *lexer) case '{': return make_token(lexer, TOKEN_LBRACE, "{"); case '}': - return match(lexer, ')') ? make_token(lexer, TOKEN_RPARBRA, "})") : make_token(lexer, TOKEN_RBRACE, "})"); + return match(lexer, ')') ? make_token(lexer, TOKEN_RPARBRA, "})") : make_token(lexer, TOKEN_RBRACE, "}"); case '(': - return match(lexer, '{') ? make_token(lexer, TOKEN_LPARBRA, "({") : make_token(lexer, TOKEN_LPAREN, ")"); + return match(lexer, '{') ? make_token(lexer, TOKEN_LPARBRA, "({") : make_token(lexer, TOKEN_LPAREN, "("); case ')': return make_token(lexer, TOKEN_RPAREN, ")"); case '[': diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index e9cedbb3c..f138a0ade 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -77,6 +77,8 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) { switch (expr->expr_kind) { + case EXPR_EXPR_BLOCK: + TODO case EXPR_IDENTIFIER: return expr->identifier_expr.decl->var.backend_ref; case EXPR_UNARY: @@ -637,12 +639,45 @@ static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *c TODO } +static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr) +{ + LLVMValueRef old_ret_out = context->return_out; + LLVMBasicBlockRef saved_expr_block = context->expr_block_exit; + + LLVMBasicBlockRef expr_block = gencontext_create_free_block(context, "expr_block.exit"); + context->expr_block_exit = expr_block; + + LLVMValueRef return_out = NULL; + if (expr->type != type_void) + { + return_out = gencontext_emit_alloca(context, llvm_type(expr->type), "blockret"); + } + context->return_out = return_out; + + Ast **stmts = expr->expr_block.stmts; + VECEACH(stmts, i) + { + gencontext_emit_stmt(context, stmts[i]); + } + gencontext_emit_br(context, expr_block); + + // Emit the exit block. + gencontext_emit_block(context, expr_block); + + context->return_out = old_ret_out; + context->expr_block_exit = saved_expr_block; + + return return_out; +} + LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { switch (expr->expr_kind) { case EXPR_POISONED: UNREACHABLE + case EXPR_EXPR_BLOCK: + return gencontext_emit_expr_block(context, expr); case EXPR_SCOPED_EXPR: return gencontext_emit_scoped_expr(context, expr); case EXPR_UNARY: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 7568c73d6..f6948e227 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -107,6 +107,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry"); context->current_block = entry; context->current_block_is_target = true; + context->expr_block_exit = NULL; context->builder = LLVMCreateBuilder(); LLVMPositionBuilderAtEnd(context->builder, entry); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 2e572007a..9e114946e 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -48,10 +48,8 @@ typedef struct LLVMValueRef alloca_point; LLVMBuilderRef builder; LLVMBasicBlockRef current_block; - bool current_block_is_target; Decl *cur_code_decl; Decl *cur_func_decl; - bool did_call_stack_save; TypeInfo *current_return_type; int block_global_unique_count; int ast_alloca_addr_space; @@ -65,6 +63,9 @@ typedef struct BreakContinue break_continue_stack[BREAK_STACK_MAX]; size_t break_continue_stack_index; LLVMValueRef return_out; + LLVMBasicBlockRef expr_block_exit; + bool current_block_is_target : 1; + bool did_call_stack_save : 1; } GenContext; diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 5a938d979..b5a586b7a 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -105,6 +105,12 @@ LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool return result; } +void gencontext_emit_jmp(GenContext *context, LLVMBasicBlockRef block) +{ + gencontext_emit_br(context, block); + LLVMBasicBlockRef post_jump_block = gencontext_create_free_block(context, "jmp"); + gencontext_emit_block(context, post_jump_block); +} static inline void gencontext_emit_return(GenContext *context, Ast *ast) { @@ -113,6 +119,18 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast) LLVMValueRef ret_value = ast->return_stmt.expr ? gencontext_emit_expr(context, ast->return_stmt.expr) : NULL; gencontext_emit_defer(context, ast->return_stmt.defer, NULL); + + // Are we in an expression block? + if (context->expr_block_exit) + { + if (context->return_out) + { + LLVMBuildStore(context->builder, ret_value, context->return_out); + } + gencontext_emit_jmp(context, context->expr_block_exit); + return; + } + if (!ret_value) { gencontext_emit_implicit_return(context); @@ -393,12 +411,6 @@ void gencontext_emit_do_stmt(GenContext *context, Ast *ast) gencontext_pop_break_continue(context); } -void gencontext_emit_jmp(GenContext *context, LLVMBasicBlockRef block) -{ - gencontext_emit_br(context, block); - LLVMBasicBlockRef post_jump_block = gencontext_create_free_block(context, "jmp"); - gencontext_emit_block(context, post_jump_block); -} void gencontext_emit_label(GenContext *context, Ast *ast) { @@ -591,8 +603,6 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) { switch (ast->ast_kind) { - case AST_FUNCTION_BLOCK_STMT: - TODO case AST_POISONED: UNREACHABLE case AST_SCOPED_STMT: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c new file mode 100644 index 000000000..c4e6089fc --- /dev/null +++ b/src/compiler/parse_expr.c @@ -0,0 +1,785 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "compiler_internal.h" +#include "parser_internal.h" + + +typedef Expr *(*ParseFn)(Context *context, Expr *); + +typedef struct +{ + ParseFn prefix; + ParseFn infix; + Precedence precedence; +} ParseRule; + +extern ParseRule rules[TOKEN_EOF + 1]; + +inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence) +{ + while (precedence <= rules[context->tok.type].precedence) + { + if (!expr_ok(left_side)) return left_side; + ParseFn infix_rule = rules[context->tok.type].infix; + left_side = infix_rule(context, left_side); + } + return left_side; +} + + +static Expr *parse_precedence(Context *context, Precedence precedence) +{ + // Get the rule for the previous token. + ParseFn prefix_rule = rules[context->tok.type].prefix; + if (prefix_rule == NULL) + { + SEMA_TOKEN_ERROR(context->tok, "An expression was expected."); + return &poisoned_expr; + } + + Expr *left_side = prefix_rule(context, NULL); + if (!expr_ok(left_side)) return left_side; + return parse_precedence_with_left_side(context, left_side, precedence); +} + +inline Expr* parse_expr(Context *context) +{ + return parse_precedence(context, PREC_ASSIGNMENT); +} + +inline Expr* parse_constant_expr(Context *context) +{ + return parse_precedence(context, PREC_TERNARY); +} + +/** + * param_list + * : parameter + * | parameter ',' parameters + * ; + * + * parameter + * : type + * | expr + * ; + * + */ +bool parse_param_list(Context *context, Expr ***result, bool allow_type) +{ + *result = NULL; + while (1) + { + TypeInfo *type = NULL; + Expr *expr = NULL; + SourceRange start = context->tok.span; + if (!parse_type_or_expr(context, &expr, &type)) return false; + if (!expr) + { + if (!allow_type) + { + sema_error_range(start, "Did not expect a type here, only expressions."); + return false; + } + expr = expr_new(EXPR_TYPE, start); + RANGE_EXTEND_PREV(expr); + expr->type_expr.type = type; + } + vec_add(*result, expr); + if (!try_consume(context, TOKEN_COMMA)) + { + return true; + } + } +} + +static Expr *parse_macro_expr(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *macro_expr = EXPR_NEW_TOKEN(EXPR_MACRO_EXPR, context->tok); + advance_and_verify(context, TOKEN_AT); + macro_expr->macro_expr = TRY_EXPR_OR(parse_precedence(context, PREC_UNARY + 1), &poisoned_expr); + return macro_expr; +} + + +static inline Expr* parse_non_assign_expr(Context *context) +{ + return parse_precedence(context, PREC_ASSIGNMENT + 1); +} + +/** + * expression_list + * : expression + * | expression_list ',' expression + * ; + * @return Ast * + */ +Expr *parse_expression_list(Context *context) +{ + Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); + if (!parse_param_list(context, &expr_list->expression_list, false)) return &poisoned_expr; + return expr_list; +} + +/** + * @param left must be null. + * @return Expr* + */ +static Expr *parse_type_identifier(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + return parse_type_identifier_with_path(context, NULL); +} +static Expr *parse_cast_expr(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CAST, context->tok); + advance_and_verify(context, TOKEN_CAST); + CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); + expr->cast_expr.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); + CONSUME_OR(TOKEN_COMMA, &poisoned_expr); + expr->cast_expr.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr); + CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); + return expr; +} + + +static Expr *parse_unary_expr(Context *context, Expr *left) +{ + assert(!left && "Did not expect a left hand side!"); + + TokenType operator_type = context->tok.type; + + Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, context->tok); + unary->unary_expr.operator = unaryop_from_token(operator_type); + Precedence rule_precedence = rules[operator_type].precedence; + advance(context); + Expr *right_side = parse_precedence(context, rule_precedence); + + CHECK_EXPR(right_side); + + unary->unary_expr.expr = right_side; + return unary; +} + +static Expr *parse_post_unary(Context *context, Expr *left) +{ + assert(expr_ok(left)); + Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, context->tok); + unary->post_expr.expr = left; + unary->post_expr.operator = post_unaryop_from_token(context->tok.type); + advance(context); + return unary; +} + +static Expr *parse_ternary_expr(Context *context, Expr *left_side) +{ + assert(expr_ok(left_side)); + Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side); + expr_ternary->ternary_expr.cond = left_side; + + // Check for elvis + if (try_consume(context, TOKEN_ELVIS)) + { + expr_ternary->ternary_expr.then_expr = NULL; + } + else + { + advance_and_verify(context, TOKEN_QUESTION); + Expr *true_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr); + expr_ternary->ternary_expr.then_expr = true_expr; + CONSUME_OR(TOKEN_COLON, &poisoned_expr); + } + + Expr *false_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr); + expr_ternary->ternary_expr.else_expr = false_expr; + return expr_ternary; +} + +/** + * grouping_expr + * : '(' expression ')' + * ; + */ +static Expr *parse_grouping_expr(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + advance_and_verify(context, TOKEN_LPAREN); + Expr *right = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); + CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); + return right; +} + +/** + * initializer + * : initializer_list + * | expr + * ; + * + * @param context + * @return the parsed expression + */ +Expr *parse_initializer(Context *context) +{ + if (context->tok.type == TOKEN_LBRACE) + { + return parse_initializer_list(context); + } + return parse_expr(context); +} + +/** + * initializer_list + * : '{' initializer_values '}' + * | '{' initializer_values ',' '}' + * ; + * + * initializer_values + * : initializer + * | initializer_values ',' initializer + * ; + * + * @param elements + * @return + */ +Expr *parse_initializer_list(Context *context) +{ + Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok); + CONSUME_OR(TOKEN_LBRACE, &poisoned_expr); + if (!parse_param_list(context, &initializer_list->initializer_expr, false)) return &poisoned_expr; + CONSUME_OR(TOKEN_RBRACE, &poisoned_expr); + return initializer_list; +} + +static Expr *parse_binary(Context *context, Expr *left_side) +{ + assert(left_side && expr_ok(left_side)); + + // Remember the operator. + TokenType operator_type = context->tok.type; + + advance(context); + + Expr *right_side; + if (context->tok.type == TOKEN_LBRACE && operator_type == TOKEN_EQ) + { + right_side = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); + } + else + { + right_side = TRY_EXPR_OR(parse_precedence(context, rules[operator_type].precedence + 1), &poisoned_expr); + } + + Expr *expr = EXPR_NEW_EXPR(EXPR_BINARY, left_side); + expr->binary_expr.operator = binaryop_from_token(operator_type); + expr->binary_expr.left = left_side; + expr->binary_expr.right = right_side; + return expr; +} + +static Expr *parse_call_expr(Context *context, Expr *left) +{ + assert(left && expr_ok(left)); + + Expr **params = NULL; + advance_and_verify(context, TOKEN_LPAREN); + if (context->tok.type != TOKEN_RPAREN) + { + if (!parse_param_list(context, ¶ms, 0)) return &poisoned_expr; + } + TRY_CONSUME_OR(TOKEN_RPAREN, "Expected the ending ')' here", &poisoned_expr); + + Expr *call = EXPR_NEW_EXPR(EXPR_CALL, left); + call->call_expr.function = left; + call->call_expr.arguments = params; + + return call; +} + + +static Expr *parse_subscript_expr(Context *context, Expr *left) +{ + assert(left && expr_ok(left)); + + advance_and_verify(context, TOKEN_LBRACKET); + Expr *index = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); + CONSUME_OR(TOKEN_RBRACKET, &poisoned_expr); + Expr *subscript_ast = EXPR_NEW_EXPR(EXPR_SUBSCRIPT, left); + subscript_ast->subscript_expr.expr = left; + subscript_ast->subscript_expr.index = index; + return subscript_ast; +} + + +static Expr *parse_access_expr(Context *context, Expr *left) +{ + assert(left && expr_ok(left)); + advance_and_verify(context, TOKEN_DOT); + Expr *access_expr = EXPR_NEW_EXPR(EXPR_ACCESS, left); + access_expr->access_expr.parent = left; + access_expr->access_expr.sub_element = context->tok; + TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", &poisoned_expr); + return access_expr; +} + + +static Expr *parse_identifier_with_path(Context *context, Path *path) +{ + Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, context->tok); + expr->identifier_expr.identifier = context->tok.string; + expr->identifier_expr.path = path; + advance(context); + return expr; +} + +static Expr *parse_identifier(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + return parse_identifier_with_path(context, NULL); +} + +static Expr *parse_type_expr(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE, context->tok); + advance_and_verify(context, TOKEN_TYPE); + CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr); + CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); + expr->type_expr.type = type; + return expr; +} + +static Expr *parse_maybe_scope(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Path *path = parse_path_prefix(context); + switch (context->tok.type) + { + case TOKEN_IDENT: + case TOKEN_CT_IDENT: + case TOKEN_CONST_IDENT: + return parse_identifier_with_path(context, path); + case TOKEN_TYPE_IDENT: + return parse_type_identifier_with_path(context, path); + default: + SEMA_TOKEN_ERROR(context->tok, "Expected a type, function or constant."); + return &poisoned_expr; + } +} + +static Expr *parse_try_expr(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok); + advance_and_verify(context, TOKEN_TRY); + try_expr->try_expr.expr = TRY_EXPR_OR(parse_precedence(context, PREC_TRY + 1), &poisoned_expr); + if (try_consume(context, TOKEN_ELSE)) + { + try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr); + } + return try_expr; +} + +static Expr *parse_integer(Context *context, Expr *left) +{ + assert(!left && "Had left hand side"); + Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + const char *string = context->tok.start; + const char *end = string + source_range_len(context->tok.span); + uint64_t i = 0; + if (string[0] == '\'') + { + union + { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + uint8_t b[8]; + } bytes; + int pos = 0; + while (++string < end - 1) + { + if (*string == '\\') + { + if (*(++string) == 'x') + { + int hex = 0; + for (int j = 0; j < 2; j++) + { + hex <<= 4U; + char c = *(++string); + if (c < 'A') + { + hex += c - '0'; + } + else if (c < 'a') + { + hex += c - 'A' + 10; + } + else + { + hex += c - 'a' + 10; + } + } + bytes.b[pos++] = hex; + continue; + } + } + bytes.b[pos++] = (unsigned)*string; + } + switch (pos) + { + case 1: + expr_int->const_expr.i = bytes.u8; + break; + case 2: + expr_int->const_expr.i = bytes.u16; + break; + case 4: + expr_int->const_expr.i = bytes.u32; + break; + case 8: + expr_int->const_expr.i = bytes.u64; + break; + } + expr_int->const_expr.type = CONST_INT; + expr_int->type = i > INT64_MAX ? type_compuint : type_compint; + expr_int->resolve_status = RESOLVE_DONE; + advance(context); + return expr_int; + } + switch (source_range_len(context->tok.span) > 2 ? string[1] : '0') + { + case 'x': + string += 2; + while (string < end) + { + char c = *(string++); + if (c == '_') continue; + if (i > (UINT64_MAX >> 4U)) + { + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); + return &poisoned_expr; + } + i <<= 4U; + if (c < 'A') + { + i += c - '0'; + } + else if (c < 'a') + { + i += c - 'A' + 10; + } + else + { + i += c - 'a' + 10; + } + } + break; + case 'o': + string += 2; + while (string < end) + { + char c = *(string++); + if (c == '_') continue; + if (i > (UINT64_MAX >> 3U)) + { + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); + return &poisoned_expr; + } + i <<= (unsigned) 3; + i += c - '0'; + } + break; + case 'b': + string += 2; + while (string < end) + { + char c = *(string++); + if (c == '_') continue; + if (i > (UINT64_MAX >> 1U)) + { + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); + return &poisoned_expr; + } + i <<= (unsigned) 1; + i += c - '0'; + } + break; + default: + while (string < end) + { + char c = *(string++); + if (c == '_') continue; + if (i > (UINT64_MAX / 10)) + { + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); + return &poisoned_expr; + } + i *= 10; + i += c - '0'; + } + break; + + } + expr_int->const_expr.i = i; + expr_int->const_expr.type = CONST_INT; + expr_int->type = i > INT64_MAX ? type_compuint : type_compint; + advance(context); + return expr_int; +} + + +static Expr *parse_double(Context *context, Expr *left) +{ + assert(!left && "Had left hand side"); + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + char *end = NULL; + // IMPROVE + long double fval = strtold(context->tok.start, &end); + if (end != source_range_len(context->tok.span) + context->tok.start) + { + SEMA_TOKEN_ERROR(context->tok, "Invalid float value"); + return &poisoned_expr; + } + advance(context); + number->const_expr.f = fval; + number->type = type_compfloat; + number->const_expr.type = CONST_FLOAT; + return number; +} + +static int append_esc_string_token(char *restrict dest, const char *restrict src, size_t *pos) +{ + int scanned = 0; + uint64_t unicode_char = 0; + switch (src[0]) + { + case 'a': + dest[(*pos)++] = '\a'; + return 1; + case 'b': + dest[(*pos)++] = '\b'; + return 1; + case 'e': + dest[(*pos)++] = 0x1b; + return 1; + case 'f': + dest[(*pos)++] = '\f'; + return 1; + case 'n': + dest[(*pos)++] = '\n'; + return 1; + case 'r': + dest[(*pos)++] = '\r'; + return 1; + case 't': + dest[(*pos)++] = '\t'; + return 1; + case 'x': + { + int h = char_to_nibble(src[1]); + int l = char_to_nibble(src[2]); + if (h < 0 || l < 0) return -1; + unicode_char = ((unsigned) h << 4U) + l; + scanned = 3; + break; + } + case 'u': + { + int x1 = char_to_nibble(src[1]); + int x2 = char_to_nibble(src[2]); + int x3 = char_to_nibble(src[3]); + int x4 = char_to_nibble(src[4]); + if (x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0) return -1; + unicode_char = ((unsigned) x1 << 12U) + ((unsigned) x2 << 8U) + ((unsigned) x3 << 4U) + x4; + scanned = 5; + break; + } + case 'U': + { + int x1 = char_to_nibble(src[1]); + int x2 = char_to_nibble(src[2]); + int x3 = char_to_nibble(src[3]); + int x4 = char_to_nibble(src[4]); + int x5 = char_to_nibble(src[5]); + int x6 = char_to_nibble(src[6]); + int x7 = char_to_nibble(src[7]); + int x8 = char_to_nibble(src[8]); + if (x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || x5 < 0 || x6 < 0 || x7 < 0 || x8 < 0) return -1; + unicode_char = ((unsigned) x1 << 28U) + ((unsigned) x2 << 24U) + ((unsigned) x3 << 20U) + ((unsigned) x4 << 16U) + + ((unsigned) x5 << 12U) + ((unsigned) x6 << 8U) + ((unsigned) x7 << 4U) + x8; + scanned = 9; + break; + } + default: + dest[(*pos)++] = src[0]; + return 1; + } + if (unicode_char < 0x80U) + { + dest[(*pos)++] = (char)unicode_char; + } + else if (unicode_char < 0x800U) + { + dest[(*pos)++] = (char)(0xC0U | (unicode_char >> 6U)); + dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU)); + } + else if (unicode_char < 0x10000U) + { + dest[(*pos)++] = (char)(0xE0U | (unicode_char >> 12U)); + dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 6U) & 0x3FU)); + dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU)); + } + else + { + dest[(*pos)++] = (char)(0xF0U | (unicode_char >> 18U)); + dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 12U) & 0x3FU)); + dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 6U) & 0x3FU)); + dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU)); + } + return scanned; +} + +static Expr *parse_string_literal(Context *context, Expr *left) +{ + assert(!left && "Had left hand side"); + Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + expr_string->resolve_status = RESOLVE_DONE; + expr_string->type = type_string; + + char *str = NULL; + size_t len = 0; + + while (context->tok.type == TOKEN_STRING) + { + char *new_string = malloc_arena(len + source_range_len(context->tok.span)); + if (str) memcpy(new_string, str, len); + str = new_string; + for (unsigned i = 1; i < source_range_len(context->tok.span) - 1; i++) + { + if (context->tok.string[i] == '\\') + { + i++; + i += append_esc_string_token(str, context->tok.string + i, &len) - 1; + continue; + } + str[len++] = context->tok.string[i]; + } + advance_and_verify(context, TOKEN_STRING); + } + + assert(str); + str[len] = '\0'; + expr_string->const_expr.string.chars = str; + expr_string->const_expr.string.len = len; + expr_string->type = type_string; + expr_string->const_expr.type = CONST_STRING; + return expr_string; +} + +static Expr *parse_bool(Context *context, Expr *left) +{ + assert(!left && "Had left hand side"); + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .type = CONST_BOOL }; + number->type = type_bool; + number->resolve_status = RESOLVE_DONE; + advance(context); + return number; +} + +static Expr *parse_nil(Context *context, Expr *left) +{ + assert(!left && "Had left hand side"); + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + number->const_expr.type = CONST_NIL; + number->type = type_voidptr; + advance(context); + return number; +} + +/** + * function_block + * : '({' stmt_list '})' + */ +static Expr* parse_expr_block(Context *context, Expr *left) +{ + assert(!left && "Had left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_EXPR_BLOCK, context->tok); + advance_and_verify(context, TOKEN_LPARBRA); + while (!try_consume(context, TOKEN_RPARBRA)) + { + Ast *stmt = parse_stmt(context); + if (!ast_ok(stmt)) return &poisoned_expr; + vec_add(expr->expr_block.stmts, stmt); + } + return expr; +} + +ParseRule rules[TOKEN_EOF + 1] = { + [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, + [TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY }, + [TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, + [TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, + [TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL }, + [TOKEN_LPARBRA] = { parse_expr_block, NULL, PREC_NONE }, + [TOKEN_TYPE] = { parse_type_expr, NULL, PREC_NONE }, + [TOKEN_CAST] = { parse_cast_expr, NULL, PREC_NONE }, + [TOKEN_TRY] = { parse_try_expr, NULL, PREC_TRY }, + //[TOKEN_SIZEOF] = { parse_sizeof, NULL, PREC_NONE }, + [TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL }, + [TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, + [TOKEN_MINUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE }, + [TOKEN_PLUS] = { NULL, parse_binary, PREC_ADDITIVE }, + [TOKEN_PLUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE }, + [TOKEN_DIV] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, + [TOKEN_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, + [TOKEN_STAR] = { parse_unary_expr, parse_binary, PREC_MULTIPLICATIVE }, + [TOKEN_MULT_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, + [TOKEN_DOT] = { NULL, parse_access_expr, PREC_CALL }, + [TOKEN_NOT] = { parse_unary_expr, NULL, PREC_UNARY }, + [TOKEN_BIT_NOT] = { parse_unary_expr, NULL, PREC_UNARY }, + [TOKEN_BIT_XOR] = { NULL, parse_binary, PREC_BIT }, + [TOKEN_BIT_OR] = { NULL, parse_binary, PREC_BIT }, + [TOKEN_AMP] = { parse_unary_expr, parse_binary, PREC_BIT }, + [TOKEN_EQEQ] = { NULL, parse_binary, PREC_RELATIONAL }, + [TOKEN_NOT_EQUAL] = { NULL, parse_binary, PREC_RELATIONAL }, + [TOKEN_GREATER] = { NULL, parse_binary, PREC_RELATIONAL }, + [TOKEN_GREATER_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, + [TOKEN_LESS] = { NULL, parse_binary, PREC_RELATIONAL }, + [TOKEN_LESS_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, + [TOKEN_SHL] = { NULL, parse_binary, PREC_SHIFT }, + [TOKEN_SHR] = { NULL, parse_binary, PREC_SHIFT }, + [TOKEN_TRUE] = { parse_bool, NULL, PREC_NONE }, + [TOKEN_FALSE] = { parse_bool, NULL, PREC_NONE }, + [TOKEN_NIL] = { parse_nil, NULL, PREC_NONE }, + [TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE }, + [TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE }, + [TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE }, + [TOKEN_AT] = { parse_macro_expr, NULL, PREC_UNARY }, + [TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, + [TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE }, + [TOKEN_FLOAT] = { parse_double, NULL, PREC_NONE }, + [TOKEN_OR] = { NULL, parse_binary, PREC_LOGICAL }, + [TOKEN_AND] = { NULL, parse_binary, PREC_LOGICAL }, + [TOKEN_EQ] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_PLUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_PLUS_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_MINUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_MINUS_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_MULT_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_MULT_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_DIV_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_BIT_XOR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_BIT_AND_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_BIT_OR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_SHR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + [TOKEN_SHL_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, +}; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c new file mode 100644 index 000000000..42322adbf --- /dev/null +++ b/src/compiler/parse_stmt.c @@ -0,0 +1,865 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "compiler_internal.h" +#include "parser_internal.h" + + +#pragma mark --- Internal functions + + +/** + * declaration_stmt + * : declaration ';' + */ +static inline Ast *parse_declaration_stmt(Context *context) +{ + Ast *decl_stmt = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok); + decl_stmt->declare_stmt = TRY_DECL_OR(parse_decl(context), &poisoned_ast); + CONSUME_OR(TOKEN_EOS, &poisoned_ast); + return decl_stmt; +} + +/** + * control_expression + * : decl_or_expr_list + * | declaration_list ';' decl_or_expr_list + * ; + */ +static inline bool parse_control_expression(Context *context, Ast **decls, Ast **exprs) +{ + *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); + + if (!try_consume(context, TOKEN_EOS)) + { + *decls = NULL; + return true; + } + + *decls = *exprs; + + *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); + + return true; +} + +static inline Ast* parse_asm_stmt(Context *context) +{ + // TODO + SEMA_TOKEN_ERROR(context->tok, "ASM not supported yet."); + return &poisoned_ast; +} + +/** + * do_stmt + * : DO statement WHILE '(' expression ')' ';' + */ +static inline Ast* parse_do_stmt(Context *context) +{ + Ast *do_ast = AST_NEW_TOKEN(AST_DO_STMT, context->tok); + + advance_and_verify(context, TOKEN_DO); + + do_ast->do_stmt.body = TRY_AST(parse_stmt(context)); + + CONSUME_OR(TOKEN_WHILE, &poisoned_ast); + + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + + CONSUME_OR(TOKEN_EOS, &poisoned_ast); + + return do_ast; +} + + +/** + * catch_stmt + * : CATCH '(' ERROR ident ')' statement + * | CATCH '(' type_expression ident ')' statement + * ; + */ +static inline Ast* parse_catch_stmt(Context *context) +{ + Ast *catch_stmt = AST_NEW_TOKEN(AST_CATCH_STMT, context->tok); + advance_and_verify(context, TOKEN_CATCH); + + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + + TypeInfo *type = NULL; + if (!try_consume(context, TOKEN_ERROR_TYPE)) + { + type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast); + } + EXPECT_IDENT_FOR_OR("error parameter", &poisoned_ast); + Decl *decl = decl_new_var(context->tok, type, VARDECL_PARAM, VISIBLE_LOCAL); + catch_stmt->catch_stmt.error_param = decl; + + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + catch_stmt->catch_stmt.body = TRY_AST(parse_stmt(context)); + return catch_stmt; +} + +/** + * defer_stmt + * : DEFER statement + */ +static inline Ast* parse_defer_stmt(Context *context) +{ + advance_and_verify(context, TOKEN_DEFER); + Ast *defer_stmt = AST_NEW_TOKEN(AST_DEFER_STMT, context->tok); + defer_stmt->defer_stmt.body = TRY_AST(parse_stmt(context)); + return defer_stmt; +} + +/** + * while_stmt + * : WHILE '(' control_expression ')' statement + */ +static inline Ast* parse_while_stmt(Context *context) +{ + Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok); + + advance_and_verify(context, TOKEN_WHILE); + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + if (!parse_control_expression(context, &while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return &poisoned_ast; + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + while_ast->while_stmt.body = TRY_AST(parse_stmt(context)); + return while_ast; +} + +/** + * if_stmt + * : IF '(' control_expression ')' statement + * | IF '(' control_expression ')' compound_statement ELSE compound_statement + * ; + */ +static inline Ast* parse_if_stmt(Context *context) +{ + Ast *if_ast = AST_NEW_TOKEN(AST_IF_STMT, context->tok); + advance_and_verify(context, TOKEN_IF); + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + if (!parse_control_expression(context, &if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return &poisoned_ast; + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + Ast *stmt = TRY_AST(parse_stmt(context)); + if_ast->if_stmt.then_body = stmt; + if (!try_consume(context, TOKEN_ELSE)) + { + return if_ast; + } + if_ast->if_stmt.else_body = TRY_AST(parse_stmt(context)); + return if_ast; +} + +static inline bool token_type_ends_case(TokenType type) +{ + return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE; +} + +static inline Ast *parse_case_stmts(Context *context) +{ + Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + while (!token_type_ends_case(context->tok.type)) + { + Ast *stmt = TRY_AST(parse_stmt(context)); + vec_add(compound->compound_stmt.stmts, stmt); + } + return compound; +} + + +/** + * case_stmt + * : CASE constant_expression ':' case_stmts + */ +static inline Ast* parse_case_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); + advance(context); + Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), &poisoned_ast); + ast->case_stmt.expr = expr; + TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); + extend_ast_with_prev_token(context, ast); + ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); + return ast; +} + +/** + * default_stmt + * : DEFAULT ':' case_stmts + */ +static inline Ast *parse_default_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok); + advance_and_verify(context, TOKEN_DEFAULT); + TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", &poisoned_ast); + extend_ast_with_prev_token(context, ast); + ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); + ast->case_stmt.value_type = CASE_VALUE_DEFAULT; + return ast; +} + +/** + * switch_body + * : case_stmt + * | default_stmt + * | case_stmt switch_body + * | default_stmt switch body + * ; + */ +static inline bool parse_switch_body(Context *context, Ast *switch_ast) +{ + Ast *result; + switch (context->tok.type) + { + case TOKEN_CASE: + result = TRY_AST_OR(parse_case_stmt(context), false); + break; + case TOKEN_DEFAULT: + result = TRY_AST_OR(parse_default_stmt(context), false); + break; + default: + SEMA_TOKEN_ERROR(context->tok, "A 'case' or 'default' would be needed here, '%.*s' is not allowed.", source_range_len(context->tok.span), context->tok.start); + return false; + } + vec_add(switch_ast->switch_stmt.cases, result); + return true; +} + + +/** + * switch + * : SWITCH '(' control_expression ')' '{' switch_body '}' + */ +static inline Ast* parse_switch_stmt(Context *context) +{ + Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok); + advance_and_verify(context, TOKEN_SWITCH); + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast; + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); + while (!try_consume(context, TOKEN_RBRACE)) + { + if (!parse_switch_body(context, switch_ast)) return &poisoned_ast; + } + return switch_ast; +} + +/** + * for_statement + * : FOR '(' decl_expr_list ';' expression ';' ')' statement + * | FOR '(' decl_expr_list ';' ';' expression_list ')' statement + * | FOR '(' decl_expr_list ';' expression ';' expression_list ')' statement + * | FOR '(' ';' expression ';' ')' statement + * | FOR '(' ';' ';' expression_list ')' statement + * | FOR '(' ';' expression ';' expression_list ')' statement + * ; + */ +static inline Ast* parse_for_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_FOR_STMT, context->tok); + advance_and_verify(context, TOKEN_FOR); + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + + if (context->tok.type != TOKEN_EOS) + { + ast->for_stmt.init = TRY_AST(parse_decl_expr_list(context)); + } + else + { + ast->for_stmt.init = NULL; + } + + CONSUME_OR(TOKEN_EOS, &poisoned_ast); + + if (context->tok.type != TOKEN_EOS) + { + ast->for_stmt.cond = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + } + + CONSUME_OR(TOKEN_EOS, &poisoned_ast); + + if (context->tok.type != TOKEN_RPAREN) + { + ast->for_stmt.incr = parse_expression_list(context); + } + + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + + extend_ast_with_prev_token(context, ast); + ast->for_stmt.body = TRY_AST(parse_stmt(context)); + return ast; +} + +/** + * goto_stmt + * : GOTO ct_ident EOS + * ; + */ +static inline Ast* parse_goto_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_GOTO_STMT, context->tok); + advance_and_verify(context, TOKEN_GOTO); + ast->goto_stmt.label_name = context->tok.string; + if (!consume_const_name(context, "label")) return &poisoned_ast; + RETURN_AFTER_EOS(ast); +} + +/** + * continue_stmt + * : CONTINUE EOS + */ +static inline Ast* parse_continue_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CONTINUE_STMT, context->tok); + advance_and_verify(context, TOKEN_CONTINUE); + RETURN_AFTER_EOS(ast); +} + +/** + * next_stmt + * : NEXT EOS + */ +static inline Ast* parse_next_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok); + advance_and_verify(context, TOKEN_NEXT); + RETURN_AFTER_EOS(ast); +} + +/** + * break_stmt + * : BREAK EOS + */ +static inline Ast* parse_break_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_BREAK_STMT, context->tok); + advance_and_verify(context, TOKEN_BREAK); + RETURN_AFTER_EOS(ast); +} + +/** + * expr_stmt + * : expression EOS + */ +static inline Ast *parse_expr_stmt(Context *context) +{ + Ast *stmt = AST_NEW_TOKEN(AST_EXPR_STMT, context->tok); + stmt->expr_stmt = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + TRY_CONSUME_EOS(); + return stmt; +} + +/** + * ct_else_stmt + * : CT_ELSE compound_stmt + */ +static inline Ast* parse_ct_else_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CT_ELSE_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_ELSE); + ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); + return ast; +} + +/** + * ct_elif_stmt + * : CT_ELIF '(' expression ')' compound_statement + */ +static inline Ast *parse_ct_elif_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CT_ELIF_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_ELIF); + + ast->ct_elif_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); + + ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); + + if (context->tok.type == TOKEN_CT_ELIF) + { + ast->ct_elif_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); + } + else if (context->tok.type == TOKEN_CT_ELSE) + { + ast->ct_elif_stmt.elif = TRY_AST(parse_ct_else_stmt(context)); + } + return ast; +} +/** + * ct_if_stmt + * : CT_IF '(' expression ')' compound_stmt + * | CT_IF '(' expression ')' compound_stmt elif_stmt + * | CT_IF '(' expression ')' compound_stmt else_stmt + * ; + */ +static inline Ast* parse_ct_if_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CT_IF_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_IF); + ast->ct_if_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); + ast->ct_if_stmt.then = TRY_AST(parse_compound_stmt(context)); + if (context->tok.type == TOKEN_CT_ELIF) + { + ast->ct_if_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); + } + else if (context->tok.type == TOKEN_CT_ELSE) + { + ast->ct_if_stmt.elif = TRY_AST(parse_ct_else_stmt(context)); + } + return ast; +} + +/** + * label_stmt + * : ct_label ':' + */ +static inline Ast *parse_label_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_LABEL, context->tok); + ast->label_stmt.name = context->tok.string; + advance_and_verify(context, TOKEN_CONST_IDENT); + advance_and_verify(context, TOKEN_COLON); + return extend_ast_with_prev_token(context, ast); +} + + +/** + * return_stmt + * : RETURN expression EOS + * | RETURN EOS + * ; + */ +static inline Ast *parse_return_stmt(Context *context) +{ + advance_and_verify(context, TOKEN_RETURN); + Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok); + ast->exit = EXIT_RETURN; + ast->return_stmt.defer = NULL; + if (try_consume(context, TOKEN_EOS)) + { + ast->return_stmt.expr = NULL; + return ast; + } + ast->return_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + RETURN_AFTER_EOS(ast); +} + +/** + * throw_stmt + * : THROW expr EOS + */ +static inline Ast *parse_throw_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_THROW_STMT, context->tok); + advance_and_verify(context, TOKEN_THROW); + ast->throw_stmt.throw_value = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + RETURN_AFTER_EOS(ast); +} + +/** + * volatile_stmt + * : VOLATILE compound_stmt + */ +static Ast *parse_volatile_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_VOLATILE_STMT, context->tok); + ast->volatile_stmt = TRY_AST_OR(parse_compound_stmt(context), &poisoned_ast); + return ast; +} + +static inline bool is_valid_try_statement(TokenType type) +{ + switch (type) + { + case TOKEN_SWITCH: + case TOKEN_IF: + case TOKEN_FOR: + case TOKEN_WHILE: + case TOKEN_DO: + case TOKEN_RETURN: + return true; + default: + return false; + } +} + +static inline Ast *parse_decl_or_expr_stmt(Context *context) +{ + Expr *expr = NULL; + TypeInfo *type = NULL; + + if (!parse_type_or_expr(context, &expr, &type)) return &poisoned_ast; + + Ast *ast; + if (expr) + { + ast = AST_NEW(AST_EXPR_STMT, expr->span); + ast->expr_stmt = expr; + } + else + { + Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, false, type), &poisoned_ast); + ast = AST_NEW(AST_DECLARE_STMT, decl->span); + ast->declare_stmt = decl; + } + CONSUME_OR(TOKEN_EOS, &poisoned_ast); + return ast; +} + +/** + * ct_for_stmt + * : CT_FOR '(' CT_IDENT IN expression ')' statement + * | CT_FOR '(' CT_IDENT, CT_IDENT IN expression ')' statement + * ; + * + * @return + */ +static inline Ast* parse_ct_for_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_FOR); + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + if (context->next_tok.type == TOKEN_COMMA) + { + ast->ct_for_stmt.index = context->tok; + TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time index variable", &poisoned_ast); + advance_and_verify(context, TOKEN_COMMA); + } + ast->ct_for_stmt.value = context->tok; + TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", &poisoned_ast); + TRY_CONSUME_OR(TOKEN_IN, "Expected 'in'.", &poisoned_ast); + ast->ct_for_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + ast->ct_for_stmt.body = TRY_AST(parse_stmt(context)); + return ast; +} + +/** + * CTSWITCH '(' expression ')' '{' ct_switch_body '}' + * + * ct_switch_body + * : ct_case_statement + * | ct_switch_body ct_case_statement + * ; + * + * ct_case_statement + * : CTCASE type_list ':' statement + * | CTDEFAULT ':' statement + * ; + * + * @return + */ +static inline Ast* parse_ct_switch_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_SWITCH); + ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); + CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); + Ast **switch_statements = NULL; + Ast *stmt = &poisoned_ast; + while (stmt) + { + switch (context->tok.type) + { + case TOKEN_CT_CASE: + stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); + advance(context); + while (1) + { + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast); + vec_add(stmt->ct_case_stmt.types, type); + if (!try_consume(context, TOKEN_COMMA)) break; + } + CONSUME_OR(TOKEN_COLON, &poisoned_ast); + stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(context), &poisoned_ast); + vec_add(switch_statements, stmt); + break; + case TOKEN_CT_DEFAULT: + stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); + advance(context); + CONSUME_OR(TOKEN_COLON, &poisoned_ast); + stmt->ct_default_stmt = TRY_AST_OR(parse_stmt(context), &poisoned_ast); + vec_add(switch_statements, stmt); + break; + case TOKEN_RBRACE: + stmt = NULL; + break; + default: + SEMA_TOKEN_ERROR(context->tok, "Expected $case or $default."); + return &poisoned_ast; + } + } + CONSUME_OR(TOKEN_RBRACE, &poisoned_ast); + ast->ct_switch_stmt.body = switch_statements; + return ast; +} + +#pragma mark --- External functions + +Ast *parse_stmt(Context *context) +{ + switch (context->tok.type) + { + case TOKEN_LBRACE: + return parse_compound_stmt(context); + case TOKEN_HALF: + case TOKEN_QUAD: + SEMA_TOKEN_ERROR(context->next_tok, "Type is unsupported by platform."); + advance(context); + return &poisoned_ast; + case TOKEN_VOID: + case TOKEN_BYTE: + case TOKEN_BOOL: + case TOKEN_CHAR: + case TOKEN_DOUBLE: + case TOKEN_FLOAT: + case TOKEN_INT: + case TOKEN_ISIZE: + case TOKEN_LONG: + case TOKEN_SHORT: + case TOKEN_UINT: + case TOKEN_ULONG: + case TOKEN_USHORT: + case TOKEN_USIZE: + case TOKEN_C_SHORT: + case TOKEN_C_INT: + case TOKEN_C_LONG: + case TOKEN_C_LONGLONG: + case TOKEN_C_USHORT: + case TOKEN_C_UINT: + case TOKEN_C_ULONG: + case TOKEN_C_ULONGLONG: + case TOKEN_TYPE_IDENT: + if (context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LBRACE) + { + return parse_expr_stmt(context); + } + return parse_declaration_stmt(context); + case TOKEN_LOCAL: // Local means declaration! + case TOKEN_CONST: // Const means declaration! + return parse_declaration_stmt(context); + case TOKEN_TYPE: + return parse_decl_or_expr_stmt(context); + case TOKEN_CONST_IDENT: + if (context->next_tok.type == TOKEN_COLON) + { + return parse_label_stmt(context); + } + return parse_expr_stmt(context); + case TOKEN_AT: + return parse_expr_stmt(context); + case TOKEN_IDENT: + if (context->next_tok.type == TOKEN_SCOPE) + { + return parse_decl_or_expr_stmt(context); + } + return parse_expr_stmt(context); + case TOKEN_RETURN: + return parse_return_stmt(context); + case TOKEN_IF: + return parse_if_stmt(context); + case TOKEN_WHILE: + return parse_while_stmt(context); + case TOKEN_DEFER: + return parse_defer_stmt(context); + case TOKEN_SWITCH: + return parse_switch_stmt(context); + case TOKEN_GOTO: + return parse_goto_stmt(context); + case TOKEN_DO: + return parse_do_stmt(context); + case TOKEN_FOR: + return parse_for_stmt(context); + case TOKEN_CATCH: + return parse_catch_stmt(context); + case TOKEN_TRY: + if (is_valid_try_statement(context->next_tok.type)) + { + Token token = context->tok; + advance(context); + Ast *stmt = TRY_AST(parse_stmt(context)); + Ast *try_ast = AST_NEW_TOKEN(AST_TRY_STMT, token); + try_ast->try_stmt = stmt; + return try_ast; + } + return parse_expr_stmt(context); + case TOKEN_CONTINUE: + return parse_continue_stmt(context); + case TOKEN_CASE: + SEMA_TOKEN_ERROR(context->tok, "'case' was found outside of 'switch', did you mismatch a '{ }' pair?"); + advance(context); + return &poisoned_ast; + case TOKEN_BREAK: + return parse_break_stmt(context); + case TOKEN_NEXT: + return parse_next_stmt(context); + case TOKEN_ASM: + return parse_asm_stmt(context); + case TOKEN_DEFAULT: + SEMA_TOKEN_ERROR(context->tok, "'default' was found outside of 'switch', did you mismatch a '{ }' pair?"); + advance(context); + return &poisoned_ast; + case TOKEN_CT_IF: + return parse_ct_if_stmt(context); + case TOKEN_CT_SWITCH: + return parse_ct_switch_stmt(context); + case TOKEN_CT_FOR: + return parse_ct_for_stmt(context); + case TOKEN_THROW: + return parse_throw_stmt(context); + case TOKEN_VOLATILE: + return parse_volatile_stmt(context); + case TOKEN_STAR: + case TOKEN_AMP: + case TOKEN_INTEGER: + case TOKEN_BIT_NOT: + case TOKEN_BIT_OR: + case TOKEN_BIT_XOR: + case TOKEN_LPAREN: + case TOKEN_MINUS: + case TOKEN_NOT: + case TOKEN_OR: + case TOKEN_PLUS: + case TOKEN_MINUSMINUS: + case TOKEN_PLUSPLUS: + case TOKEN_HASH_IDENT: + case TOKEN_CT_IDENT: + case TOKEN_STRING: + case TOKEN_REAL: + case TOKEN_CAST: + case TOKEN_FALSE: + case TOKEN_NIL: + case TOKEN_TRUE: + case TOKEN_LPARBRA: + return parse_expr_stmt(context); + case TOKEN_INVALID_TOKEN: + advance(context); + return &poisoned_ast; + case TOKEN_COLON: + case TOKEN_COMMA: + case TOKEN_EQ: + case TOKEN_GREATER: + case TOKEN_DIV: + case TOKEN_DOLLAR: + case TOKEN_DOT: + case TOKEN_HASH: + case TOKEN_LESS: + case TOKEN_LBRACKET: + case TOKEN_MOD: + case TOKEN_QUESTION: + case TOKEN_AND: + case TOKEN_ARROW: + case TOKEN_BIT_AND_ASSIGN: + case TOKEN_BIT_OR_ASSIGN: + case TOKEN_BIT_XOR_ASSIGN: + case TOKEN_PLUS_MOD: + case TOKEN_MINUS_MOD: + case TOKEN_MULT_MOD: + case TOKEN_DIV_ASSIGN: + case TOKEN_DOTDOT: + case TOKEN_ELVIS: + case TOKEN_EQEQ: + case TOKEN_GREATER_EQ: + case TOKEN_LESS_EQ: + case TOKEN_MINUS_ASSIGN: + case TOKEN_MOD_ASSIGN: + case TOKEN_MULT_ASSIGN: + case TOKEN_NOT_EQUAL: + case TOKEN_PLUS_ASSIGN: + case TOKEN_ELIPSIS: + case TOKEN_SCOPE: + case TOKEN_SHR: + case TOKEN_SHL: + case TOKEN_MULT_MOD_ASSIGN: + case TOKEN_PLUS_MOD_ASSIGN: + case TOKEN_MINUS_MOD_ASSIGN: + case TOKEN_SHR_ASSIGN: + case TOKEN_SHL_ASSIGN: + case TOKEN_ALIAS: + case TOKEN_AS: + case TOKEN_ELSE: + case TOKEN_ENUM: + case TOKEN_ERROR_TYPE: + case TOKEN_FUNC: + case TOKEN_GENERIC: + case TOKEN_IMPORT: + case TOKEN_MACRO: + case TOKEN_MODULE: + case TOKEN_PUBLIC: + case TOKEN_EXTERN: + case TOKEN_STRUCT: + case TOKEN_THROWS: + case TOKEN_TYPEDEF: + case TOKEN_UNION: + case TOKEN_UNTIL: + case TOKEN_ATTRIBUTE: + case TOKEN_VAR: + case TOKEN_AT_PARAM: + case TOKEN_AT_THROWS: + case TOKEN_AT_RETURN: + case TOKEN_AT_ENSURE: + case TOKEN_AT_REQUIRE: + case TOKEN_AT_PURE: + case TOKEN_AT_CONST: + case TOKEN_AT_REQPARSE: + case TOKEN_AT_DEPRECATED: + case TOKEN_DOCS_START: + case TOKEN_DOCS_END: + case TOKEN_DOCS_EOL: + case TOKEN_DOC_COMMENT: + case TOKEN_COMMENT: + case TOKEN_DOCS_LINE: + case TOKEN_CT_CASE: + case TOKEN_CT_ELIF: + case TOKEN_CT_ELSE: + case TOKEN_CT_DEFAULT: + case TOKEN_RPARBRA: + case TOKEN_IN: + SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); + advance(context); + return &poisoned_ast; + break; + case TOKEN_RPAREN: + case TOKEN_RBRACE: + case TOKEN_RBRACKET: + SEMA_TOKEN_ERROR(context->tok, "Mismatched '%s' found.", token_type_to_string(context->tok.type)); + advance(context); + return &poisoned_ast; + case TOKEN_EOS: + advance(context); + return AST_NEW_TOKEN(AST_NOP_STMT, context->tok); + case TOKEN_EOF: + sema_error_at(context->tok.span.loc - 1, "Reached the end of the file when expecting a statement."); + return &poisoned_ast; + } +} + + + +/** + * compound_stmt + * : '{' stmt_list '}' + * ; + * + * stmt_list + * : stmt + * | stmt stmt_list + * ; + * + * @param context + * @return a compound statement + */ +Ast* parse_compound_stmt(Context *context) +{ + CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); + Ast *ast = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + while (!try_consume(context, TOKEN_RBRACE)) + { + Ast *stmt = TRY_AST(parse_stmt(context)); + ast->compound_stmt.stmts = VECADD(ast->compound_stmt.stmts, stmt); + } + return ast; +} diff --git a/src/compiler/parser.c b/src/compiler/parser.c index bf52bc6aa..d14c4cd2f 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -3,29 +3,15 @@ // a copy of which can be found in the LICENSE file. #include "compiler_internal.h" +#include "parser_internal.h" Token module = { .type = TOKEN_INVALID_TOKEN }; -static Ast *parse_stmt(Context *context); -static Expr *parse_expr(Context *context); -static Expr *parse_paren_expr(Context *context); -static Expr *parse_precedence(Context *context, Precedence precedence); -static Expr *parse_initializer_list(Context *context); -static Expr *parse_initializer(Context *context); -static bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr); static Decl *parse_top_level(Context *context); -typedef Expr *(*ParseFn)(Context *context, Expr *); -typedef struct -{ - ParseFn prefix; - ParseFn infix; - Precedence precedence; -} ParseRule; +#pragma mark --- Context storage -extern ParseRule rules[TOKEN_EOF + 1]; - -void context_store_lexer_state(Context *context) +static void context_store_lexer_state(Context *context) { assert(!context->stored.in_lookahead && "Nested lexer store is forbidden"); context->stored.in_lookahead = true; @@ -38,7 +24,7 @@ void context_store_lexer_state(Context *context) context->stored.next_lead_comment = context->next_lead_comment; } -void context_restore_lexer_state(Context *context) +static void context_restore_lexer_state(Context *context) { assert(context->stored.in_lookahead && "Tried to restore missing stored state."); context->stored.in_lookahead = false; @@ -52,6 +38,13 @@ void context_restore_lexer_state(Context *context) context->prev_tok_end = context->tok.span.end_loc; } +#pragma mark --- Parser base methods + +/** + * Advance to the next non-comment token. + * + * @param context the current context. + */ inline void advance(Context *context) { context->lead_comment = context->next_lead_comment; @@ -66,6 +59,7 @@ inline void advance(Context *context) if (context->next_tok.type == TOKEN_INVALID_TOKEN) continue; + // Skip comments during lookahead if (context->stored.in_lookahead && (context->next_tok.type == TOKEN_COMMENT || context->next_tok.type == TOKEN_DOC_COMMENT)) { @@ -78,6 +72,8 @@ inline void advance(Context *context) vec_add(context->comments, context->next_tok); continue; } + + // Handle doc comments if (context->next_tok.type == TOKEN_DOC_COMMENT) { SourcePosition current_position = source_file_find_position_in_file(context->file, @@ -114,26 +110,6 @@ inline void advance(Context *context) } -inline void advance_and_verify(Context *context, TokenType token_type) -{ - assert(context->tok.type == token_type); - advance(context); -} - -static inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence) -{ - while (precedence <= rules[context->tok.type].precedence) - { - if (!expr_ok(left_side)) return left_side; - ParseFn infix_rule = rules[context->tok.type].infix; - left_side = infix_rule(context, left_side); - } - return left_side; -} - -// --- Parser base methods - - bool try_consume(Context *context, TokenType type) { if (context->tok.type == type) @@ -170,22 +146,6 @@ static inline bool consume_ident(Context *context, const char* name) return false; } -static inline bool expect_ident(Context *context, const char* name) -{ - switch (context->tok.type) - { - case TOKEN_IDENT: - return true; - case TOKEN_TYPE_IDENT: - case TOKEN_CONST_IDENT: - SEMA_TOKEN_ERROR(context->tok, "A %s cannot start with a capital letter.", name); - return false; - default: - SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name); - return false; - } -} - static bool consume_type_name(Context *context, const char* type) { if (context->tok.type == TOKEN_IDENT) @@ -202,7 +162,7 @@ static bool consume_type_name(Context *context, const char* type) return true; } -static bool consume_const_name(Context *context, const char* type) +bool consume_const_name(Context *context, const char* type) { if (context->tok.type == TOKEN_IDENT || context->tok.type == TOKEN_TYPE_IDENT) { @@ -243,14 +203,6 @@ static void recover_top_level(Context *context) } -static inline bool expect(Context *context, TokenType token_type) -{ - if (token_type == context->tok.type) return true; - - SEMA_TOKEN_ERROR(context->tok, "Expected '%s'.", token_type_to_string(token_type)); - return false; -} - void error_at_current(Context *context, const char* message, ...) { va_list args; @@ -259,29 +211,7 @@ void error_at_current(Context *context, const char* message, ...) va_end(args); } -// --- Parsing - -#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(context, _name)) return _res; } while(0) -#define EXPECT_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; } while(0) -#define CONSUME_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; advance(context); } while(0) -#define TRY_EXPECT_OR(_tok, _message, _type) do { if (context->tok.type != _tok) { SEMA_TOKEN_ERROR(context->tok, _message); return _type; } } while(0) -#define TRY_CONSUME_OR(_tok, _message, _type) do { if (!consume(context, _tok, _message)) return _type; } while(0) -#define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR(_tok, _message, &poisoned_ast) -#define TRY_CONSUME_EOS_OR(_res) TRY_CONSUME_OR(TOKEN_EOS, "Expected ';'", _res) -#define TRY_CONSUME_EOS() TRY_CONSUME_EOS_OR(&poisoned_ast) -#define RETURN_AFTER_EOS(_ast) extend_ast_with_prev_token(context, ast); TRY_CONSUME_EOS_OR(&poisoned_ast); return _ast -#define TRY_CONSUME_LBRACE() TRY_CONSUME(TOKEN_LBRACE, "Expected '{'") - -#define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; }) -#define TRY_AST(_ast_stmt) TRY_AST_OR(_ast_stmt, &poisoned_ast) -#define TRY_EXPR_OR(_expr_stmt, _res) ({ Expr* _expr = (_expr_stmt); if (!expr_ok(_expr)) return _res; _expr; }) -#define TRY_TYPE_OR(_type_stmt, _res) ({ TypeInfo* _type = (_type_stmt); if (!type_info_ok(_type)) return _res; _type; }) -#define TRY_DECL_OR(_decl_stmt, _res) ({ Decl* _decl = (_decl_stmt); if (!decl_ok(_decl)) return _res; _decl; }) - -#define COMMA_RPAREN_OR(_res) \ -do { if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RPAREN) { \ -SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0) - +#pragma mark --- Parse paths static inline Path *parse_module_path(Context *context) { @@ -316,33 +246,7 @@ static inline Path *parse_module_path(Context *context) return path_create_from_string(scratch_ptr, offset, span); } - -static Ast* parse_compound_stmt(Context *context) -{ - CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); - Ast *ast = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); - while (!try_consume(context, TOKEN_RBRACE)) - { - Ast *stmt = TRY_AST(parse_stmt(context)); - ast->compound_stmt.stmts = VECADD(ast->compound_stmt.stmts, stmt); - } - return ast; -} - -static Ast* parse_function_block(Context *context) -{ - TODO; - CONSUME_OR(TOKEN_LPARBRA, &poisoned_ast); - Ast *ast = AST_NEW_TOKEN(AST_FUNCTION_BLOCK_STMT, context->tok); - while (!try_consume(context, TOKEN_RPARBRA)) - { - Ast *stmt = TRY_AST(parse_stmt(context)); - ast->function_block_stmt.stmts = VECADD(ast->function_block_stmt.stmts, stmt); - } - return ast; -} - -static Path *parse_path_prefix(Context *context) +Path *parse_path_prefix(Context *context) { if (context->tok.type != TOKEN_IDENT || context->next_tok.type != TOKEN_SCOPE) return NULL; @@ -382,6 +286,8 @@ static Path *parse_path_prefix(Context *context) return path; } +#pragma mark --- Type parsing + /** * base_type * : VOID @@ -559,7 +465,7 @@ static inline TypeInfo *parse_array_type_index(Context *context, TypeInfo *type) * Assume already stepped into. * @return Type, poisoned if parsing is invalid. */ -static TypeInfo *parse_type_expression(Context *context) +TypeInfo *parse_type_expression(Context *context) { TypeInfo *type_info = parse_base_type(context); while (type_info_ok(type_info)) @@ -585,13 +491,15 @@ static TypeInfo *parse_type_expression(Context *context) return type_info; } +#pragma mark --- Decl parsing + /** * Parse ident ('=' expr)? * @param local * @param type * @return */ -static inline Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) +Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) { if (context->tok.type == TOKEN_LPAREN) { @@ -625,7 +533,7 @@ static inline Decl *parse_decl_after_type(Context *context, bool local, TypeInfo * * @return Decl* (poisoned on error) */ -static Decl *parse_decl(Context *context) +Decl *parse_decl(Context *context) { bool local = context->tok.type == TOKEN_LOCAL; bool constant = context->tok.type == TOKEN_CONST; @@ -641,53 +549,79 @@ static Decl *parse_decl(Context *context) return decl; } + /** - * declaration_stmt - * : declaration ';' + * const_decl + * : 'const' CT_IDENT '=' const_expr ';' + * | 'const' type IDENT '=' const_expr ';' + * ; + */ +static inline Decl *parse_const_declaration(Context *context, Visibility visibility) +{ + advance_and_verify(context, TOKEN_CONST); + + Decl *decl = decl_new_var(context->tok, NULL, VARDECL_CONST, visibility); + // Parse the compile time constant. + if (context->tok.type == TOKEN_CT_IDENT) + { + if (!is_all_upper(context->tok.string)) + { + SEMA_TOKEN_ERROR(context->tok, "Compile time constants must be all upper characters."); + return &poisoned_decl; + } + } + else + { + if (!consume_const_name(context, "constant")) return &poisoned_decl; + decl->var.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); + } + + CONSUME_OR(TOKEN_EQ, &poisoned_decl); + + decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); + + CONSUME_OR(TOKEN_EOS, &poisoned_decl); + return decl; +} + +/** + * global_declaration + * : type_expression IDENT ';' + * | type_expression IDENT '=' expression ';' * ; * - * @return Ast* (poisoned if parsing fails) + * @param visibility + * @return true if parsing succeeded */ -static Ast *parse_declaration_stmt(Context *context) +static inline Decl *parse_global_declaration(Context *context, Visibility visibility) { - Ast *decl_stmt = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok); - decl_stmt->declare_stmt = TRY_DECL_OR(parse_decl(context), &poisoned_ast); - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return decl_stmt; -} + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); + Decl *decl = decl_new_var(context->tok, type, VARDECL_GLOBAL, visibility); -/** - * expr_stmt ::= expression EOS - * @return Ast* poisoned if expression fails to parse. - */ -static Ast *parse_expr_stmt(Context *context) -{ - Ast *stmt = AST_NEW_TOKEN(AST_EXPR_STMT, context->tok); - stmt->expr_stmt = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); - TRY_CONSUME_EOS(); - return stmt; -} + if (!consume_ident(context, "global variable")) return &poisoned_decl; - -/** - * expression_list - * : expression - * | expression_list ',' expression - * ; - * @return Ast * - */ -static inline Expr *parse_expression_list(Context *context) -{ - Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); - do + if (try_consume(context, TOKEN_EQ)) { - Expr *expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); - vec_add(expr_list->expression_list, expr); - } while (try_consume(context, TOKEN_COMMA)); - return expr_list; + decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); + } + TRY_CONSUME_EOS_OR(&poisoned_decl); + return decl; } +static inline Decl *parse_incremental_array(Context *context) +{ + Token name = context->tok; + advance_and_verify(context, TOKEN_IDENT); + + CONSUME_OR(TOKEN_PLUS_ASSIGN, &poisoned_decl); + Decl *decl = decl_new(DECL_ARRAY_VALUE, name, VISIBLE_LOCAL); + decl->incr_array_decl = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); + return decl; +} + + + /** * decl_expr_list * : expression @@ -698,7 +632,7 @@ static inline Expr *parse_expression_list(Context *context) * * @return bool */ -static inline Ast *parse_decl_expr_list(Context *context) +Ast *parse_decl_expr_list(Context *context) { Ast *decl_expr = AST_NEW_TOKEN(AST_DECL_EXPR_LIST, context->tok); decl_expr->decl_expr_stmt = NULL; @@ -725,457 +659,13 @@ static inline Ast *parse_decl_expr_list(Context *context) return extend_ast_with_prev_token(context, decl_expr); } -/** - * control_expression - * : decl_or_expr_list - * | declaration_list ';' decl_or_expr_list - * ; - * - * @return true if it succeeds - */ -static inline bool parse_control_expression(Context *context, Ast **decls, Ast **exprs) -{ - *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); - - if (!try_consume(context, TOKEN_EOS)) - { - *decls = NULL; - return true; - } - - *decls = *exprs; - - *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); - - return true; -} - -/** - * if_stmt - * : IF '(' control_expression ')' statement - * | IF '(' control_expression ')' compound_statement ELSE compound_statement - * ; - * - * @return - */ -static inline Ast* parse_if_stmt(Context *context) -{ - Ast *if_ast = AST_NEW_TOKEN(AST_IF_STMT, context->tok); - advance_and_verify(context, TOKEN_IF); - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (!parse_control_expression(context, &if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return &poisoned_ast; - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - Ast *stmt = TRY_AST(parse_stmt(context)); - if_ast->if_stmt.then_body = stmt; - if (!try_consume(context, TOKEN_ELSE)) - { - return if_ast; - } - if_ast->if_stmt.else_body = TRY_AST(parse_stmt(context)); - return if_ast; -} - -/** - * while_stmt - * : WHILE '(' control_expression ')' statement - * ; - * - * @param context - * @return the while AST - */ -static inline Ast* parse_while_stmt(Context *context) -{ - Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok); - - advance_and_verify(context, TOKEN_WHILE); - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (!parse_control_expression(context, &while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return &poisoned_ast; - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - while_ast->while_stmt.body = TRY_AST(parse_stmt(context)); - return while_ast; -} - -/** - * defer - * : DEFER statement - * | DEFER catch statement - * ; - * @return the defer AST - */ -static inline Ast* parse_defer_stmt(Context *context) -{ - Ast *defer_stmt = AST_NEW_TOKEN(AST_DEFER_STMT, context->tok); - advance_and_verify(context, TOKEN_DEFER); - // TODO catch - defer_stmt->defer_stmt.body = TRY_AST(parse_stmt(context)); - return defer_stmt; -} - -/** - * catch - * : catch '(' ERROR ident ')' statement - * | catch '(' type_expression ident ')' statement - * ; - * - * @return Ast* - */ -static inline Ast* parse_catch_stmt(Context *context) -{ - Ast *catch_stmt = AST_NEW_TOKEN(AST_CATCH_STMT, context->tok); - advance_and_verify(context, TOKEN_CATCH); - - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - - TypeInfo *type = NULL; - if (!try_consume(context, TOKEN_ERROR_TYPE)) - { - type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast); - } - EXPECT_IDENT_FOR_OR("error parameter", &poisoned_ast); - Decl *decl = decl_new_var(context->tok, type, VARDECL_PARAM, VISIBLE_LOCAL); - catch_stmt->catch_stmt.error_param = decl; - - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - catch_stmt->catch_stmt.body = TRY_AST(parse_stmt(context)); - return catch_stmt; -} - - -static inline Ast* parse_asm_stmt(Context *context __unused) -{ - TODO -} - -/** - * do_stmt - * : DO statement WHILE '(' expression ')' ';' - */ -static inline Ast* parse_do_stmt(Context *context) -{ - Ast *do_ast = AST_NEW_TOKEN(AST_DO_STMT, context->tok); - - advance_and_verify(context, TOKEN_DO); - - do_ast->do_stmt.body = TRY_AST(parse_stmt(context)); - - CONSUME_OR(TOKEN_WHILE, &poisoned_ast); - - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - - return do_ast; -} - - - -/** - * for_statement - * : FOR '(' decl_expr_list ';' expression ';' ')' statement - * | FOR '(' decl_expr_list ';' ';' expression_list ')' statement - * | FOR '(' decl_expr_list ';' expression ';' expression_list ')' statement - * | FOR '(' ';' expression ';' ')' statement - * | FOR '(' ';' ';' expression_list ')' statement - * | FOR '(' ';' expression ';' expression_list ')' statement - * ; - * - * @return Ast* - */ -static inline Ast* parse_for_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_FOR_STMT, context->tok); - advance_and_verify(context, TOKEN_FOR); - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - - if (context->tok.type != TOKEN_EOS) - { - ast->for_stmt.init = TRY_AST(parse_decl_expr_list(context)); - } - else - { - ast->for_stmt.init = NULL; - } - - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - - if (context->tok.type != TOKEN_EOS) - { - ast->for_stmt.cond = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); - } - - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - - if (context->tok.type != TOKEN_RPAREN) - { - ast->for_stmt.incr = parse_expression_list(context); - } - - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - - extend_ast_with_prev_token(context, ast); - ast->for_stmt.body = TRY_AST(parse_stmt(context)); - return ast; -} - -static inline Expr* parse_constant_expr(Context *context) -{ - return parse_precedence(context, PREC_TERNARY); -} - -static inline Ast* parse_goto_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_GOTO_STMT, context->tok); - advance_and_verify(context, TOKEN_GOTO); - ast->goto_stmt.label_name = context->tok.string; - if (!consume_const_name(context, "label")) return &poisoned_ast; - RETURN_AFTER_EOS(ast); -} - -static inline Ast* parse_continue_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CONTINUE_STMT, context->tok); - advance_and_verify(context, TOKEN_CONTINUE); - RETURN_AFTER_EOS(ast); -} - -static inline Ast* parse_next_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok); - advance_and_verify(context, TOKEN_NEXT); - RETURN_AFTER_EOS(ast); -} - -static inline Ast* parse_break_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_BREAK_STMT, context->tok); - advance_and_verify(context, TOKEN_BREAK); - RETURN_AFTER_EOS(ast); -} - - -/** - * CTSWITCH '(' expression ')' '{' ct_switch_body '}' - * - * ct_switch_body - * : ct_case_statement - * | ct_switch_body ct_case_statement - * ; - * - * ct_case_statement - * : CTCASE type_list ':' statement - * | CTDEFAULT ':' statement - * ; - * - * @return - */ -static inline Ast* parse_ct_switch_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_SWITCH); - ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); - CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); - Ast **switch_statements = NULL; - Ast *stmt = &poisoned_ast; - while (stmt) - { - switch (context->tok.type) - { - case TOKEN_CT_CASE: - stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); - advance(context); - while (1) - { - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast); - vec_add(stmt->ct_case_stmt.types, type); - if (!try_consume(context, TOKEN_COMMA)) break; - } - CONSUME_OR(TOKEN_COLON, &poisoned_ast); - stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(context), &poisoned_ast); - vec_add(switch_statements, stmt); - break; - case TOKEN_CT_DEFAULT: - stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); - advance(context); - CONSUME_OR(TOKEN_COLON, &poisoned_ast); - stmt->ct_default_stmt = TRY_AST_OR(parse_stmt(context), &poisoned_ast); - vec_add(switch_statements, stmt); - break; - case TOKEN_RBRACE: - stmt = NULL; - break; - default: - SEMA_TOKEN_ERROR(context->tok, "Expected $case or $default."); - return &poisoned_ast; - } - } - CONSUME_OR(TOKEN_RBRACE, &poisoned_ast); - ast->ct_switch_stmt.body = switch_statements; - return ast; -} - -static inline Ast* parse_ct_else_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CT_ELSE_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_ELSE); - ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); - return ast; -} - -/** - * ct_elif_stmt - * : $elif '(' expression ')' compound_statement - * @return - */ -static inline Ast *parse_ct_elif_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CT_ELIF_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_ELIF); - - ast->ct_elif_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); - - ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); - - if (context->tok.type == TOKEN_CT_ELIF) - { - ast->ct_elif_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); - } - else if (context->tok.type == TOKEN_CT_ELSE) - { - ast->ct_elif_stmt.elif = TRY_AST(parse_ct_else_stmt(context)); - } - return ast; -} - -/** - * ct_if_stmt - * : $if '(' expression ')' compound_stmt - * | $if '(' expression ')' compound_stmt elif_stmt - * | $if '(' expression ')' compound_stmt else_stmt - * ; - * - * @return Ast* - */ -static inline Ast* parse_ct_if_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CT_IF_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_IF); - ast->ct_if_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); - ast->ct_if_stmt.then = TRY_AST(parse_compound_stmt(context)); - if (context->tok.type == TOKEN_CT_ELIF) - { - ast->ct_if_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); - } - else if (context->tok.type == TOKEN_CT_ELSE) - { - ast->ct_if_stmt.elif = TRY_AST(parse_ct_else_stmt(context)); - } - return ast; -} - - -/** - * ct_for_stmt - * : CTFOR '(' CT_IDENT IN expression ')' statement - * | CTFOR '(' CT_IDENT, CT_IDENT IN expression ')' statement - * ; - * - * @return - */ -static inline Ast* parse_ct_for_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok); - advance_and_verify(context, TOKEN_CT_FOR); - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (context->next_tok.type == TOKEN_COMMA) - { - ast->ct_for_stmt.index = context->tok; - TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time index variable", &poisoned_ast); - advance_and_verify(context, TOKEN_COMMA); - } - ast->ct_for_stmt.value = context->tok; - TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", &poisoned_ast); - TRY_CONSUME_OR(TOKEN_IN, "Expected 'in'.", &poisoned_ast); - ast->ct_for_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - ast->ct_for_stmt.body = TRY_AST(parse_stmt(context)); - return ast; -} - - -/** - * return_stmt - * : RETURN expression ';' - * | RETURN ';' - * ; - * - * @return Ast* if parsing fails it is poisoned - */ -static Ast *parse_return_stmt(Context *context) -{ - - advance_and_verify(context, TOKEN_RETURN); - Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok); - ast->exit = EXIT_RETURN; - ast->return_stmt.defer = NULL; - if (try_consume(context, TOKEN_EOS)) - { - ast->return_stmt.expr = NULL; - return ast; - } - ast->return_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); - RETURN_AFTER_EOS(ast); -} - -static Ast *parse_throw_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_THROW_STMT, context->tok); - advance_and_verify(context, TOKEN_THROW); - ast->throw_stmt.throw_value = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); - RETURN_AFTER_EOS(ast); -} - -static Ast *parse_volatile_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_VOLATILE_STMT, context->tok); - ast->volatile_stmt = TRY_AST_OR(parse_compound_stmt(context), &poisoned_ast); - return ast; -} - - - -bool is_valid_try_statement(TokenType type) -{ - switch (type) - { - case TOKEN_SWITCH: - case TOKEN_IF: - case TOKEN_FOR: - case TOKEN_WHILE: - case TOKEN_DO: - case TOKEN_RETURN: - return true; - default: - return false; - } -} - -static inline Ast *parse_label_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_LABEL, context->tok); - ast->label_stmt.name = context->tok.string; - advance_and_verify(context, TOKEN_CONST_IDENT); - advance_and_verify(context, TOKEN_COLON); - return extend_ast_with_prev_token(context, ast); -} static inline bool is_expr_after_type_ident(Context *context) { return context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LPAREN; } -static bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr) +bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr) { switch (context->tok.type) { @@ -1253,677 +743,9 @@ static bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **typ } -static inline Ast *parse_decl_or_expr_stmt(Context *context) -{ - Expr *expr = NULL; - TypeInfo *type = NULL; - - if (!parse_type_or_expr(context, &expr, &type)) return &poisoned_ast; - - Ast *ast; - if (expr) - { - ast = AST_NEW(AST_EXPR_STMT, expr->span); - ast->expr_stmt = expr; - } - else - { - Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, false, type), &poisoned_ast); - ast = AST_NEW(AST_DECLARE_STMT, decl->span); - ast->declare_stmt = decl; - } - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return ast; -} - -static inline bool token_type_ends_case(TokenType type) -{ - return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE; -} - -static inline Ast *parse_case_stmts(Context *context) -{ - Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); - while (!token_type_ends_case(context->tok.type)) - { - Ast *stmt = TRY_AST(parse_stmt(context)); - vec_add(compound->compound_stmt.stmts, stmt); - } - return compound; -} - -/** - * case_stmt - * : CASE constant_expression ':' - * - * @return Ast* - */ -static inline Ast* parse_case_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); - advance(context); - Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), &poisoned_ast); - ast->case_stmt.expr = expr; - TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); - extend_ast_with_prev_token(context, ast); - ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); - return ast; -} - -static Ast *parse_default_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok); - advance_and_verify(context, TOKEN_DEFAULT); - TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", &poisoned_ast); - extend_ast_with_prev_token(context, ast); - ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); - ast->case_stmt.value_type = CASE_VALUE_DEFAULT; - return ast; -} - -static inline bool parse_switch_body(Context *context, Ast *switch_ast) -{ - Ast *result; - switch (context->tok.type) - { - case TOKEN_CASE: - result = TRY_AST_OR(parse_case_stmt(context), false); - break; - case TOKEN_DEFAULT: - result = TRY_AST_OR(parse_default_stmt(context), false); - break; - default: - SEMA_TOKEN_ERROR(context->tok, "A 'case' or 'default' would be needed here, '%.*s' is not allowed.", source_range_len(context->tok.span), context->tok.start); - return false; - } - vec_add(switch_ast->switch_stmt.cases, result); - return true; -} -/** - * switch - * : SWITCH '(' control_expression ')' compound_statement - * - * @return - */ -static inline Ast* parse_switch_stmt(Context *context) -{ - Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok); - advance_and_verify(context, TOKEN_SWITCH); - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast; - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); - while (!try_consume(context, TOKEN_RBRACE)) - { - if (!parse_switch_body(context, switch_ast)) return &poisoned_ast; - } - return switch_ast; -} - - -static Ast *parse_stmt(Context *context) -{ - switch (context->tok.type) - { - case TOKEN_LBRACE: - return parse_compound_stmt(context); - case TOKEN_LPARBRA: - return parse_function_block(context); - case TOKEN_HALF: - case TOKEN_QUAD: - SEMA_TOKEN_ERROR(context->next_tok, "Type is unsupported by platform."); - advance(context); - return &poisoned_ast; - case TOKEN_VOID: - case TOKEN_BYTE: - case TOKEN_BOOL: - case TOKEN_CHAR: - case TOKEN_DOUBLE: - case TOKEN_FLOAT: - case TOKEN_INT: - case TOKEN_ISIZE: - case TOKEN_LONG: - case TOKEN_SHORT: - case TOKEN_UINT: - case TOKEN_ULONG: - case TOKEN_USHORT: - case TOKEN_USIZE: - case TOKEN_C_SHORT: - case TOKEN_C_INT: - case TOKEN_C_LONG: - case TOKEN_C_LONGLONG: - case TOKEN_C_USHORT: - case TOKEN_C_UINT: - case TOKEN_C_ULONG: - case TOKEN_C_ULONGLONG: - case TOKEN_TYPE_IDENT: - if (context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LBRACE) - { - return parse_expr_stmt(context); - } - return parse_declaration_stmt(context); - case TOKEN_LOCAL: // Local means declaration! - case TOKEN_CONST: // Const means declaration! - return parse_declaration_stmt(context); - case TOKEN_TYPE: - return parse_decl_or_expr_stmt(context); - case TOKEN_CONST_IDENT: - if (context->next_tok.type == TOKEN_COLON) - { - return parse_label_stmt(context); - } - return parse_expr_stmt(context); - case TOKEN_AT: - return parse_expr_stmt(context); - case TOKEN_IDENT: - if (context->next_tok.type == TOKEN_SCOPE) - { - return parse_decl_or_expr_stmt(context); - } - return parse_expr_stmt(context); - case TOKEN_RETURN: - return parse_return_stmt(context); - case TOKEN_IF: - return parse_if_stmt(context); - case TOKEN_WHILE: - return parse_while_stmt(context); - case TOKEN_DEFER: - return parse_defer_stmt(context); - case TOKEN_SWITCH: - return parse_switch_stmt(context); - case TOKEN_GOTO: - return parse_goto_stmt(context); - case TOKEN_DO: - return parse_do_stmt(context); - case TOKEN_FOR: - return parse_for_stmt(context); - case TOKEN_CATCH: - return parse_catch_stmt(context); - case TOKEN_TRY: - if (is_valid_try_statement(context->next_tok.type)) - { - Token token = context->tok; - advance(context); - Ast *stmt = TRY_AST(parse_stmt(context)); - Ast *try_ast = AST_NEW_TOKEN(AST_TRY_STMT, token); - try_ast->try_stmt = stmt; - return try_ast; - } - return parse_expr_stmt(context); - case TOKEN_CONTINUE: - return parse_continue_stmt(context); - case TOKEN_CASE: - SEMA_TOKEN_ERROR(context->tok, "'case' was found outside of 'switch', did you mismatch a '{ }' pair?"); - advance(context); - return &poisoned_ast; - case TOKEN_BREAK: - return parse_break_stmt(context); - case TOKEN_NEXT: - return parse_next_stmt(context); - case TOKEN_ASM: - return parse_asm_stmt(context); - case TOKEN_DEFAULT: - SEMA_TOKEN_ERROR(context->tok, "'default' was found outside of 'switch', did you mismatch a '{ }' pair?"); - advance(context); - return &poisoned_ast; - case TOKEN_CT_IF: - return parse_ct_if_stmt(context); - case TOKEN_CT_SWITCH: - return parse_ct_switch_stmt(context); - case TOKEN_CT_FOR: - return parse_ct_for_stmt(context); - case TOKEN_THROW: - return parse_throw_stmt(context); - case TOKEN_VOLATILE: - return parse_volatile_stmt(context); - case TOKEN_STAR: - case TOKEN_AMP: - case TOKEN_INTEGER: - case TOKEN_BIT_NOT: - case TOKEN_BIT_OR: - case TOKEN_BIT_XOR: - case TOKEN_LPAREN: - case TOKEN_MINUS: - case TOKEN_NOT: - case TOKEN_OR: - case TOKEN_PLUS: - case TOKEN_MINUSMINUS: - case TOKEN_PLUSPLUS: - case TOKEN_HASH_IDENT: - case TOKEN_CT_IDENT: - case TOKEN_STRING: - case TOKEN_REAL: - case TOKEN_CAST: - case TOKEN_FALSE: - case TOKEN_NIL: - case TOKEN_TRUE: - return parse_expr_stmt(context); - case TOKEN_INVALID_TOKEN: - advance(context); - return &poisoned_ast; - case TOKEN_COLON: - case TOKEN_COMMA: - case TOKEN_EQ: - case TOKEN_GREATER: - case TOKEN_DIV: - case TOKEN_DOLLAR: - case TOKEN_DOT: - case TOKEN_HASH: - case TOKEN_LESS: - case TOKEN_LBRACKET: - case TOKEN_MOD: - case TOKEN_QUESTION: - case TOKEN_AND: - case TOKEN_ARROW: - case TOKEN_BIT_AND_ASSIGN: - case TOKEN_BIT_OR_ASSIGN: - case TOKEN_BIT_XOR_ASSIGN: - case TOKEN_PLUS_MOD: - case TOKEN_MINUS_MOD: - case TOKEN_MULT_MOD: - case TOKEN_DIV_ASSIGN: - case TOKEN_DOTDOT: - case TOKEN_ELVIS: - case TOKEN_EQEQ: - case TOKEN_GREATER_EQ: - case TOKEN_LESS_EQ: - case TOKEN_MINUS_ASSIGN: - case TOKEN_MOD_ASSIGN: - case TOKEN_MULT_ASSIGN: - case TOKEN_NOT_EQUAL: - case TOKEN_PLUS_ASSIGN: - case TOKEN_ELIPSIS: - case TOKEN_SCOPE: - case TOKEN_SHR: - case TOKEN_SHL: - case TOKEN_MULT_MOD_ASSIGN: - case TOKEN_PLUS_MOD_ASSIGN: - case TOKEN_MINUS_MOD_ASSIGN: - case TOKEN_SHR_ASSIGN: - case TOKEN_SHL_ASSIGN: - case TOKEN_ALIAS: - case TOKEN_AS: - case TOKEN_ELSE: - case TOKEN_ENUM: - case TOKEN_ERROR_TYPE: - case TOKEN_FUNC: - case TOKEN_GENERIC: - case TOKEN_IMPORT: - case TOKEN_MACRO: - case TOKEN_MODULE: - case TOKEN_PUBLIC: - case TOKEN_EXTERN: - case TOKEN_STRUCT: - case TOKEN_THROWS: - case TOKEN_TYPEDEF: - case TOKEN_UNION: - case TOKEN_UNTIL: - case TOKEN_ATTRIBUTE: - case TOKEN_VAR: - case TOKEN_AT_PARAM: - case TOKEN_AT_THROWS: - case TOKEN_AT_RETURN: - case TOKEN_AT_ENSURE: - case TOKEN_AT_REQUIRE: - case TOKEN_AT_PURE: - case TOKEN_AT_CONST: - case TOKEN_AT_REQPARSE: - case TOKEN_AT_DEPRECATED: - case TOKEN_DOCS_START: - case TOKEN_DOCS_END: - case TOKEN_DOCS_EOL: - case TOKEN_DOC_COMMENT: - case TOKEN_COMMENT: - case TOKEN_DOCS_LINE: - case TOKEN_CT_CASE: - case TOKEN_CT_ELIF: - case TOKEN_CT_ELSE: - case TOKEN_CT_DEFAULT: - case TOKEN_RPARBRA: - case TOKEN_IN: - SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); - advance(context); - return &poisoned_ast; - break; - case TOKEN_RPAREN: - case TOKEN_RBRACE: - case TOKEN_RBRACKET: - SEMA_TOKEN_ERROR(context->tok, "Mismatched '%s' found.", token_type_to_string(context->tok.type)); - advance(context); - return &poisoned_ast; - case TOKEN_EOS: - advance(context); - return AST_NEW_TOKEN(AST_NOP_STMT, context->tok); - case TOKEN_EOF: - sema_error_at(context->tok.span.loc - 1, "Reached the end of the file when expecting a statement."); - return &poisoned_ast; - } - UNREACHABLE -} - - -/** - * - * module_param - * : CT_IDENT - * | HASH_IDENT - * ; - * - * module_params - * : module_param - * | module_params ',' module_param - * ; - */ -static inline bool parse_optional_module_params(Context *context, Token **tokens) -{ - - *tokens = NULL; - - if (!try_consume(context, TOKEN_LPAREN)) return true; - - if (try_consume(context, TOKEN_RPAREN)) - { - SEMA_TOKEN_ERROR(context->tok, "Generic parameter list cannot be empty."); - return false; - } - - // No params - while (1) - { - switch (context->tok.type) - { - case TOKEN_IDENT: - sema_error_range(context->next_tok.span, "The module parameter must be a $ or #-prefixed name, did you forgot the '$'?"); - return false; - case TOKEN_COMMA: - sema_error_range(context->next_tok.span, "Unexpected ','"); - return false; - case TOKEN_CT_IDENT: - case TOKEN_HASH_IDENT: - case TOKEN_TYPE_IDENT: - break; - default: - SEMA_TOKEN_ERROR(context->tok, "Only generic parameters are allowed here as parameters to the module."); - return false; - } - *tokens = VECADD(*tokens, context->next_tok); - advance(context); - if (!try_consume(context, TOKEN_COMMA)) - { - return consume(context, TOKEN_RPAREN, "Expected ')'."); - } - } -} - -/** - * module - * : MODULE path ';' - * | MODULE path '(' module_params ')' ';' - */ -static inline void parse_module(Context *context) -{ - - if (!try_consume(context, TOKEN_MODULE)) - { - context_set_module_from_filename(context); - return; - } - - Path *path = parse_module_path(context); - - // Expect the module name - if (!path) - { - path = CALLOCS(Path); - path->len = strlen("INVALID"); - path->module = "INVALID"; - path->span = INVALID_RANGE; - context_set_module(context, path, NULL); - recover_top_level(context); - return; - } - - // Is this a generic module? - Token *generic_parameters = NULL; - if (!parse_optional_module_params(context, &generic_parameters)) - { - context_set_module(context, path, generic_parameters); - recover_top_level(context); - return; - } - context_set_module(context, path, generic_parameters); - TRY_CONSUME_EOS_OR(); -} - - -/** - * Only call this if the first '(' has been detected. - * - * macro_invocation_list ::= '(' ')' - * | '(' macro_parameter (',' macro_parameter)* ')' - * - * macro_parameter ::= type - * | type_or_expr - * | expr - */ -static inline bool parse_macro_parameter_list(Context *context, Expr*** result) -{ - TODO - advance_and_verify(context, TOKEN_LPAREN); - *result = NULL; - while (try_consume(context, TOKEN_RPAREN)) - { - if (try_consume(context, TOKEN_COMMA)) - { - sema_error_range(context->tok.span, "There was an empty value here, did you accidentally add a ',' too many?"); - return false; - } - Expr *expr = NULL;// TODO parse_expr(context); - if (expr->expr_kind == EXPR_POISONED) return false; - *result = VECADD(*result, expr); - COMMA_RPAREN_OR(false); - } -} - -/** - * import_selective - * : import_spec - * | import_spec AS import_spec - * ; - * - * import_spec - * : IDENT - * | TYPE - * | MACRO - * | CONST - * ; - * - * @return true if import succeeded - */ -static inline bool parse_import_selective(Context *context, Path *path) -{ - if (!token_is_symbol(context->tok.type)) - { - SEMA_TOKEN_ERROR(context->tok, "Expected a symbol name here, the syntax is 'import : '."); - return false; - } - Token symbol = context->tok; - advance(context); - // Alias? - if (!try_consume(context, TOKEN_AS)) - { - return context_add_import(context, path, symbol, EMPTY_TOKEN); - } - if (context->tok.type != symbol.type) - { - if (!token_is_symbol(context->tok.type)) - { - SEMA_TOKEN_ERROR(context->tok, "Expected a symbol name here, the syntax is 'import : AS '."); - return false; - } - SEMA_TOKEN_ERROR(context->tok, "Expected the alias be the same type of name as the symbol aliased."); - return false; - } - Token alias = context->tok; - advance(context); - return context_add_import(context, path, symbol, alias); - -} - -/** - * - * import - * : IMPORT PATH ';' - * | IMPORT PATH ':' import_selective ';' - * | IMPORT IDENT AS IDENT LOCAL ';' - * | IMPORT IDENT LOCAL ';' - * - * import_list - * : import_selective - * | import_list ',' import_selective - * ; - * - * @return true if import succeeded - */ -static inline bool parse_import(Context *context) -{ - advance_and_verify(context, TOKEN_IMPORT); - - if (context->tok.type != TOKEN_IDENT) - { - SEMA_TOKEN_ERROR(context->tok, "Import statement should be followed by the name of the module to import."); - return false; - } - - Path *path = parse_module_path(context); - if (context->tok.type == TOKEN_COLON) - { - while (1) - { - if (!parse_import_selective(context, path)) return false; - if (!try_consume(context, TOKEN_COMMA)) break; - } - } - else - { - context_add_import(context, path, EMPTY_TOKEN, EMPTY_TOKEN); - } - TRY_CONSUME_EOS_OR(false); - return true; -} - - - -static Expr *parse_precedence(Context *context, Precedence precedence) -{ - // Get the rule for the previous token. - ParseFn prefix_rule = rules[context->tok.type].prefix; - if (prefix_rule == NULL) - { - SEMA_TOKEN_ERROR(context->tok, "An expression was expected."); - return &poisoned_expr; - } - - Expr *left_side = prefix_rule(context, NULL); - if (!expr_ok(left_side)) return left_side; - return parse_precedence_with_left_side(context, left_side, precedence); -} - -static inline Expr* parse_non_assign_expr(Context *context) -{ - return parse_precedence(context, PREC_ASSIGNMENT + 1); -} - -static inline Expr* parse_expr(Context *context) -{ - return parse_precedence(context, PREC_ASSIGNMENT); -} - -static inline Expr *parse_paren_expr(Context *context) -{ - CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); - Expr *expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); - return expr; -} - -/** - * imports - * : import_decl - * | imports import_decl - * ; - */ -static inline void parse_imports(Context *context) -{ - - while (context->tok.type == TOKEN_IMPORT) - { - if (!parse_import(context)) recover_top_level(context); - } -} - - - -/** - * const_decl - * : 'const' CT_IDENT '=' const_expr ';' - * | 'const' type IDENT '=' const_expr ';' - * ; - */ -static inline Decl *parse_const_declaration(Context *context, Visibility visibility) -{ - advance_and_verify(context, TOKEN_CONST); - - Decl *decl = decl_new_var(context->tok, NULL, VARDECL_CONST, visibility); - // Parse the compile time constant. - if (context->tok.type == TOKEN_CT_IDENT) - { - if (!is_all_upper(context->tok.string)) - { - SEMA_TOKEN_ERROR(context->tok, "Compile time constants must be all upper characters."); - return &poisoned_decl; - } - } - else - { - if (!consume_const_name(context, "constant")) return &poisoned_decl; - decl->var.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); - } - - CONSUME_OR(TOKEN_EQ, &poisoned_decl); - - decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); - - CONSUME_OR(TOKEN_EOS, &poisoned_decl); - return decl; -} - -/** - * global_declaration - * : type_expression IDENT ';' - * | type_expression IDENT '=' expression ';' - * ; - * - * @param visibility - * @return true if parsing succeeded - */ -static inline Decl *parse_global_declaration(Context *context, Visibility visibility) -{ - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); - - Decl *decl = decl_new_var(context->tok, type, VARDECL_GLOBAL, visibility); - - if (!consume_ident(context, "global variable")) return &poisoned_decl; - - if (try_consume(context, TOKEN_EQ)) - { - decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); - } - TRY_CONSUME_EOS_OR(&poisoned_decl); - return decl; -} +#pragma mark --- Parse parameters & throws & attributes /** @@ -1951,8 +773,8 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl) Attr *attr = malloc_arena(sizeof(Attr)); - attr->name = context->tok; - attr->path = path; + attr->name = context->tok; + attr->path = path; TRY_CONSUME_OR(TOKEN_IDENT, "Expected an attribute", false); @@ -1961,20 +783,155 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl) attr->expr = TRY_EXPR_OR(parse_paren_expr(context), false); } const char *name= attr->name.string; - VECEACH(parent_decl->attributes, i) - { - Attr *other_attr = parent_decl->attributes[i]; - if (other_attr->name.string == name) - { - SEMA_TOKEN_ERROR(attr->name, "Repeat of attribute '%s' here.", name); - return false; - } - } - parent_decl->attributes = VECADD(parent_decl->attributes, attr); + VECEACH(parent_decl->attributes, i) + { + Attr *other_attr = parent_decl->attributes[i]; + if (other_attr->name.string == name) + { + SEMA_TOKEN_ERROR(attr->name, "Repeat of attribute '%s' here.", name); + return false; + } + } + parent_decl->attributes = VECADD(parent_decl->attributes, attr); } return true; } + + +/** + * throw_declaration + * : THROWS + * | THROWS error_list + * ; + * + * opt_throw_declaration + * : throw_declaration + * | + * ; + * + */ +static inline bool parse_opt_throw_declaration(Context *context, Visibility visibility, FunctionSignature *signature) +{ + if (context->tok.type == TOKEN_THROW) + { + SEMA_TOKEN_ERROR(context->tok, "Did you mean 'throws'?"); + return false; + } + + if (!try_consume(context, TOKEN_THROWS)) return true; + if (context->tok.type != TOKEN_TYPE_IDENT && context->tok.type != TOKEN_IDENT) + { + signature->throw_any = true; + return true; + } + Decl **throws = NULL; + while (1) + { + TypeInfo *type_info = parse_base_type(context); + if (!type_info_ok(type_info)) return false; + Decl *throw = decl_new(DECL_THROWS, context->tok, visibility); + throw->throws = type_info; + VECADD(throws, throw); + if (!try_consume(context, TOKEN_COMMA)) break; + } + switch (context->tok.type) + { + case TOKEN_TYPE_IDENT: + SEMA_TOKEN_ERROR(context->tok, "Expected ',' between each error type."); + return false; + case TOKEN_IDENT: + case TOKEN_CONST_IDENT: + SEMA_TOKEN_ERROR(context->tok, "Expected an error type."); + return false; + default: + break; + } + signature->throws = throws; + return true; +} + + +/** + * param_declaration + * : type_expression + * | type_expression IDENT + * | type_expression IDENT '=' initializer + * ; + */ +static inline bool parse_param_decl(Context *context, Visibility parent_visibility, Decl*** parameters, bool type_only) +{ + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), false); + Decl *param = decl_new_var(context->tok, type, VARDECL_PARAM, parent_visibility); + + if (!try_consume(context, TOKEN_IDENT)) + { + param->name = NULL; + } + + const char *name = param->name; + + if (!name && !type_only) + { + SEMA_TOKEN_ERROR(context->tok, "The function parameter must be named."); + return false; + } + if (name && try_consume(context, TOKEN_EQ)) + { + param->var.init_expr = TRY_EXPR_OR(parse_initializer(context), false); + } + + *parameters = VECADD(*parameters, param); + return true; +} + +/** + * + * parameter_type_list + * : parameter_list + * | parameter_list ',' ELLIPSIS + * | parameter_list ',' type_expression ELLIPSIS + * ; + * + * opt_parameter_type_list + * : '(' ')' + * | '(' parameter_type_list ')' + * ; + * + * parameter_list + * : param_declaration + * | parameter_list ',' param_declaration + * ; + * + */ +static inline bool parse_opt_parameter_type_list(Context *context, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) +{ + CONSUME_OR(TOKEN_LPAREN, false); + while (!try_consume(context, TOKEN_RPAREN)) + { + if (try_consume(context, TOKEN_ELIPSIS)) + { + signature->variadic = true; + } + else + { + if (!parse_param_decl(context, parent_visibility, &(signature->params), is_interface)) return false; + } + if (!try_consume(context, TOKEN_COMMA)) + { + EXPECT_OR(TOKEN_RPAREN, false); + } + if (signature->variadic) + { + SEMA_TOKEN_ERROR(context->tok, "Variadic arguments should be the last in a parameter list."); + return false; + } + } + return true; +} + +#pragma mark --- Parse types + /** * Expect pointer to after '{' * @@ -2194,137 +1151,6 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi } -/** - * param_declaration - * : type_expression - * | type_expression IDENT - * | type_expression IDENT '=' initializer - * ; - */ -static inline bool parse_param_decl(Context *context, Visibility parent_visibility, Decl*** parameters, bool type_only) -{ - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), false); - Decl *param = decl_new_var(context->tok, type, VARDECL_PARAM, parent_visibility); - - if (!try_consume(context, TOKEN_IDENT)) - { - param->name = NULL; - } - - const char *name = param->name; - - if (!name && !type_only) - { - SEMA_TOKEN_ERROR(context->tok, "The function parameter must be named."); - return false; - } - if (name && try_consume(context, TOKEN_EQ)) - { - param->var.init_expr = TRY_EXPR_OR(parse_initializer(context), false); - } - - *parameters = VECADD(*parameters, param); - return true; -} - - - -/** - * throw_declaration - * : THROWS - * | THROWS error_list - * ; - * - * opt_throw_declaration - * : throw_declaration - * | - * ; - * - */ -static inline bool parse_opt_throw_declaration(Context *context, Visibility visibility, FunctionSignature *signature) -{ - if (context->tok.type == TOKEN_THROW) - { - SEMA_TOKEN_ERROR(context->tok, "Did you mean 'throws'?"); - return false; - } - - if (!try_consume(context, TOKEN_THROWS)) return true; - if (context->tok.type != TOKEN_TYPE_IDENT && context->tok.type != TOKEN_IDENT) - { - signature->throw_any = true; - return true; - } - Decl **throws = NULL; - while (1) - { - TypeInfo *type_info = parse_base_type(context); - if (!type_info_ok(type_info)) return false; - Decl *throw = decl_new(DECL_THROWS, context->tok, visibility); - throw->throws = type_info; - VECADD(throws, throw); - if (!try_consume(context, TOKEN_COMMA)) break; - } - switch (context->tok.type) - { - case TOKEN_TYPE_IDENT: - SEMA_TOKEN_ERROR(context->tok, "Expected ',' between each error type."); - return false; - case TOKEN_IDENT: - case TOKEN_CONST_IDENT: - SEMA_TOKEN_ERROR(context->tok, "Expected an error type."); - return false; - default: - break; - } - signature->throws = throws; - return true; -} - -/** - * - * parameter_type_list - * : parameter_list - * | parameter_list ',' ELLIPSIS - * | parameter_list ',' type_expression ELLIPSIS - * ; - * - * opt_parameter_type_list - * : '(' ')' - * | '(' parameter_type_list ')' - * ; - * - * parameter_list - * : param_declaration - * | parameter_list ',' param_declaration - * ; - * - */ -static inline bool parse_opt_parameter_type_list(Context *context, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) -{ - CONSUME_OR(TOKEN_LPAREN, false); - while (!try_consume(context, TOKEN_RPAREN)) - { - if (try_consume(context, TOKEN_ELIPSIS)) - { - signature->variadic = true; - } - else - { - if (!parse_param_decl(context, parent_visibility, &(signature->params), is_interface)) return false; - } - if (!try_consume(context, TOKEN_COMMA)) - { - EXPECT_OR(TOKEN_RPAREN, false); - } - if (signature->variadic) - { - SEMA_TOKEN_ERROR(context->tok, "Variadic arguments should be the last in a parameter list."); - return false; - } - } - return true; -} static AttributeDomains TOKEN_TO_ATTR[TOKEN_EOF + 1] = { [TOKEN_FUNC] = ATTR_FUNC, @@ -2483,6 +1309,157 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil } +/** + * error_declaration + * : ERROR TYPE_IDENT '{' error_list '}' + * ; + * + */ +static inline Decl *parse_error_declaration(Context *context, Visibility visibility) +{ + advance_and_verify(context, TOKEN_ERROR_TYPE); + + Decl *error_decl = decl_new_with_type(context->tok, DECL_ERROR, visibility); + + if (!consume_type_name(context, "error type")) return &poisoned_decl; + + CONSUME_OR(TOKEN_LBRACE, &poisoned_decl); + + while (context->tok.type == TOKEN_CONST_IDENT) + { + Decl *err_constant = decl_new(DECL_ERROR_CONSTANT, context->tok, error_decl->visibility); + + err_constant->error_constant.parent = error_decl; + VECEACH(error_decl->error.error_constants, i) + { + Decl *other_constant = error_decl->error.error_constants[i]; + if (other_constant->name == context->tok.string) + { + SEMA_TOKEN_ERROR(context->tok, "This error is declared twice."); + SEMA_PREV(other_constant, "The previous declaration was here."); + decl_poison(err_constant); + decl_poison(error_decl); + break; + } + } + error_decl->error.error_constants = VECADD(error_decl->error.error_constants, err_constant); + advance_and_verify(context, TOKEN_CONST_IDENT); + if (!try_consume(context, TOKEN_COMMA)) break; + } + if (context->tok.type == TOKEN_TYPE_IDENT || context->tok.type == TOKEN_IDENT) + { + SEMA_TOKEN_ERROR(context->tok, "Errors must be all upper case."); + return &poisoned_decl; + } + CONSUME_OR(TOKEN_RBRACE, &poisoned_decl); + return error_decl; +} + +/** + * enum_spec + * : type + * | type '(' opt_parameter_type_list ')' + * ; + */ +static inline bool parse_enum_spec(Context *context, TypeInfo **type_ref, Decl*** parameters_ref, Visibility parent_visibility) +{ + *type_ref = TRY_TYPE_OR(parse_base_type(context), false); + if (!try_consume(context, TOKEN_LPAREN)) return true; + CONSUME_OR(TOKEN_LPAREN, false); + while (!try_consume(context, TOKEN_RPAREN)) + { + if (!parse_param_decl(context, parent_visibility, parameters_ref, false)) return false; + if (!try_consume(context, TOKEN_COMMA)) + { + EXPECT_OR(TOKEN_RPAREN, false); + } + } + return true; +} + +/** + * Expect current at enum name. + * + * enum + * : ENUM type_ident '{' enum_body '}' + * | ENUM type_ident ':' enum_spec '{' enum_body '}' + * ; + * + * enum_body + * : enum_def + * | enum_def ',' enum_body + * | enum_body ',' + * ; + * + * enum_def + * : CAPS_IDENT + * | CAPS_IDENT '=' const_expr + * | CAPS_IDENT '(' expr_list ')' + * | CAPS_IDENT '(' expr_list ')' '=' const_expr + * ; + * + */ +static inline Decl *parse_enum_declaration(Context *context, Visibility visibility) +{ + advance_and_verify(context, TOKEN_ENUM); + + Decl *decl = decl_new_with_type(context->tok, DECL_ENUM, visibility); + + if (!consume_type_name(context, "enum")) return &poisoned_decl; + + TypeInfo *type = NULL; + if (try_consume(context, TOKEN_COLON)) + { + if (!parse_enum_spec(context, &type, &decl->enums.parameters, visibility)) return &poisoned_decl; + } + + CONSUME_OR(TOKEN_LBRACE, false); + + decl->enums.type_info = type ? type : type_info_new_base(type_int); + while (!try_consume(context, TOKEN_RBRACE)) + { + Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, context->tok, decl->visibility); + enum_const->enum_constant.parent = decl; + VECEACH(decl->enums.values, i) + { + Decl *other_constant = decl->enums.values[i]; + if (other_constant->name == context->tok.string) + { + SEMA_TOKEN_ERROR(context->tok, "This enum constant is declared twice."); + SEMA_PREV(other_constant, "The previous declaration was here."); + decl_poison(enum_const); + break; + } + } + if (!consume_const_name(context, "enum constant")) + { + return &poisoned_decl; + } + if (try_consume(context, TOKEN_LPAREN)) + { + Expr **result = NULL; + if (!parse_param_list(context, &result, true)) return &poisoned_decl; + enum_const->enum_constant.args = result; + CONSUME_OR(TOKEN_RPAREN, &poisoned_decl); + } + if (try_consume(context, TOKEN_EQ)) + { + enum_const->enum_constant.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_decl); + } + vec_add(decl->enums.values, enum_const); + // Allow trailing ',' + if (!try_consume(context, TOKEN_COMMA)) + { + EXPECT_OR(TOKEN_RBRACE, &poisoned_decl); + } + } + return decl; +} + +#pragma mark --- Parse function + + + /** * Starts after 'func' * @@ -2533,12 +1510,12 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit func->name_span = context->tok.span; advance_and_verify(context, TOKEN_IDENT); - if (!parse_opt_parameter_type_list(context, visibility, &(func->func.function_signature), is_interface)) return &poisoned_decl; + if (!parse_opt_parameter_type_list(context, visibility, &(func->func.function_signature), is_interface)) return &poisoned_decl; - if (!parse_opt_throw_declaration(context, visibility, &(func->func.function_signature))) return &poisoned_decl; + if (!parse_opt_throw_declaration(context, visibility, &(func->func.function_signature))) return &poisoned_decl; - // TODO remove - is_interface = context->tok.type == TOKEN_EOS; + // TODO remove + is_interface = context->tok.type == TOKEN_EOS; if (is_interface) { @@ -2559,110 +1536,8 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit return func; } -/** - * error_declaration - * : ERROR TYPE_IDENT '{' error_list '}' - * ; - * - */ -static inline Decl *parse_error_declaration(Context *context, Visibility visibility) -{ - advance_and_verify(context, TOKEN_ERROR_TYPE); - Decl *error_decl = decl_new_with_type(context->tok, DECL_ERROR, visibility); - - if (!consume_type_name(context, "error type")) return &poisoned_decl; - - CONSUME_OR(TOKEN_LBRACE, &poisoned_decl); - - while (context->tok.type == TOKEN_CONST_IDENT) - { - Decl *err_constant = decl_new(DECL_ERROR_CONSTANT, context->tok, error_decl->visibility); - - err_constant->error_constant.parent = error_decl; - VECEACH(error_decl->error.error_constants, i) - { - Decl *other_constant = error_decl->error.error_constants[i]; - if (other_constant->name == context->tok.string) - { - SEMA_TOKEN_ERROR(context->tok, "This error is declared twice."); - SEMA_PREV(other_constant, "The previous declaration was here."); - decl_poison(err_constant); - decl_poison(error_decl); - break; - } - } - error_decl->error.error_constants = VECADD(error_decl->error.error_constants, err_constant); - advance_and_verify(context, TOKEN_CONST_IDENT); - if (!try_consume(context, TOKEN_COMMA)) break; - } - if (context->tok.type == TOKEN_TYPE_IDENT || context->tok.type == TOKEN_IDENT) - { - SEMA_TOKEN_ERROR(context->tok, "Errors must be all upper case."); - return &poisoned_decl; - } - CONSUME_OR(TOKEN_RBRACE, &poisoned_decl); - return error_decl; -} - -/** - * Expect current at enum name. - * - * enum ::= ENUM TYPE_NAME (':' type)? '{' enum_def (',' enum_def)* ','? '}' - * - * enum_def ::= CAPS_IDENT ('=' const_expr)? - * - * TODO enum extra data? - */ -static inline Decl *parse_enum_declaration(Context *context, Visibility visibility) -{ - advance_and_verify(context, TOKEN_ENUM); - - Decl *decl = decl_new_with_type(context->tok, DECL_ENUM, visibility); - - if (!consume_type_name(context, "enum")) return &poisoned_decl; - - TypeInfo *type = NULL; - if (try_consume(context, TOKEN_COLON)) - { - type = TRY_TYPE_OR(parse_base_type(context), &poisoned_decl); - } - - CONSUME_OR(TOKEN_LBRACE, false); - - decl->enums.type_info = type ? type : type_info_new_base(type_int); - while (!try_consume(context, TOKEN_RBRACE)) - { - Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, context->tok, decl->visibility); - enum_const->enum_constant.parent = decl; - VECEACH(decl->enums.values, i) - { - Decl *other_constant = decl->enums.values[i]; - if (other_constant->name == context->tok.string) - { - SEMA_TOKEN_ERROR(context->tok, "This enum constant is declared twice."); - SEMA_PREV(other_constant, "The previous declaration was here."); - decl_poison(enum_const); - break; - } - } - if (!consume_const_name(context, "enum constant")) - { - return &poisoned_decl; - } - if (try_consume(context, TOKEN_EQ)) - { - enum_const->enum_constant.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_decl); - } - decl->enums.values = VECADD(decl->enums.values, enum_const); - // Allow trailing ',' - if (!try_consume(context, TOKEN_COMMA)) - { - EXPECT_OR(TOKEN_RBRACE, &poisoned_decl); - } - } - return decl; -} +#pragma mark --- Parse CT conditional code static inline bool parse_conditional_top_level(Context *context, Decl ***decls) { @@ -2712,16 +1587,6 @@ static inline Decl *parse_ct_if_top_level(Context *context) return ct; } -static inline Decl *parse_incremental_array(Context *context) -{ - Token name = context->tok; - advance_and_verify(context, TOKEN_IDENT); - - CONSUME_OR(TOKEN_PLUS_ASSIGN, &poisoned_decl); - Decl *decl = decl_new(DECL_ARRAY_VALUE, name, VISIBLE_LOCAL); - decl->incr_array_decl = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); - return decl; -} static inline bool check_no_visibility_before(Context *context, Visibility visibility) { @@ -2825,7 +1690,211 @@ static inline Decl *parse_top_level(Context *context) } } -void parse_current(Context *context) +#pragma mark --- Parse import and module + + +/** + * + * module_param + * : CT_IDENT + * | HASH_IDENT + * ; + * + * module_params + * : module_param + * | module_params ',' module_param + * ; + */ +static inline bool parse_optional_module_params(Context *context, Token **tokens) +{ + + *tokens = NULL; + + if (!try_consume(context, TOKEN_LPAREN)) return true; + + if (try_consume(context, TOKEN_RPAREN)) + { + SEMA_TOKEN_ERROR(context->tok, "Generic parameter list cannot be empty."); + return false; + } + + // No params + while (1) + { + switch (context->tok.type) + { + case TOKEN_IDENT: + sema_error_range(context->next_tok.span, "The module parameter must be a $ or #-prefixed name, did you forgot the '$'?"); + return false; + case TOKEN_COMMA: + sema_error_range(context->next_tok.span, "Unexpected ','"); + return false; + case TOKEN_CT_IDENT: + case TOKEN_HASH_IDENT: + case TOKEN_TYPE_IDENT: + break; + default: + SEMA_TOKEN_ERROR(context->tok, "Only generic parameters are allowed here as parameters to the module."); + return false; + } + *tokens = VECADD(*tokens, context->next_tok); + advance(context); + if (!try_consume(context, TOKEN_COMMA)) + { + return consume(context, TOKEN_RPAREN, "Expected ')'."); + } + } +} + + +/** + * module + * : MODULE path ';' + * | MODULE path '(' module_params ')' ';' + */ +static inline void parse_module(Context *context) +{ + + if (!try_consume(context, TOKEN_MODULE)) + { + context_set_module_from_filename(context); + return; + } + + Path *path = parse_module_path(context); + + // Expect the module name + if (!path) + { + path = CALLOCS(Path); + path->len = strlen("INVALID"); + path->module = "INVALID"; + path->span = INVALID_RANGE; + context_set_module(context, path, NULL); + recover_top_level(context); + return; + } + + // Is this a generic module? + Token *generic_parameters = NULL; + if (!parse_optional_module_params(context, &generic_parameters)) + { + context_set_module(context, path, generic_parameters); + recover_top_level(context); + return; + } + context_set_module(context, path, generic_parameters); + TRY_CONSUME_EOS_OR(); +} + +/** + * import_selective + * : import_spec + * | import_spec AS import_spec + * ; + * + * import_spec + * : IDENT + * | TYPE + * | MACRO + * | CONST + * ; + * + * @return true if import succeeded + */ +static inline bool parse_import_selective(Context *context, Path *path) +{ + if (!token_is_symbol(context->tok.type)) + { + SEMA_TOKEN_ERROR(context->tok, "Expected a symbol name here, the syntax is 'import : '."); + return false; + } + Token symbol = context->tok; + advance(context); + // Alias? + if (!try_consume(context, TOKEN_AS)) + { + return context_add_import(context, path, symbol, EMPTY_TOKEN); + } + if (context->tok.type != symbol.type) + { + if (!token_is_symbol(context->tok.type)) + { + SEMA_TOKEN_ERROR(context->tok, "Expected a symbol name here, the syntax is 'import : AS '."); + return false; + } + SEMA_TOKEN_ERROR(context->tok, "Expected the alias be the same type of name as the symbol aliased."); + return false; + } + Token alias = context->tok; + advance(context); + return context_add_import(context, path, symbol, alias); + +} + +/** + * + * import + * : IMPORT PATH ';' + * | IMPORT PATH ':' import_selective ';' + * | IMPORT IDENT AS IDENT LOCAL ';' + * | IMPORT IDENT LOCAL ';' + * + * import_list + * : import_selective + * | import_list ',' import_selective + * ; + * + * @return true if import succeeded + */ +static inline bool parse_import(Context *context) +{ + advance_and_verify(context, TOKEN_IMPORT); + + if (context->tok.type != TOKEN_IDENT) + { + SEMA_TOKEN_ERROR(context->tok, "Import statement should be followed by the name of the module to import."); + return false; + } + + Path *path = parse_module_path(context); + if (context->tok.type == TOKEN_COLON) + { + while (1) + { + if (!parse_import_selective(context, path)) return false; + if (!try_consume(context, TOKEN_COMMA)) break; + } + } + else + { + context_add_import(context, path, EMPTY_TOKEN, EMPTY_TOKEN); + } + TRY_CONSUME_EOS_OR(false); + return true; +} + + +/** + * imports + * : import_decl + * | imports import_decl + * ; + */ +static inline void parse_imports(Context *context) +{ + + while (context->tok.type == TOKEN_IMPORT) + { + if (!parse_import(context)) recover_top_level(context); + } +} + + + +#pragma mark --- Extern functions + +static inline void parse_current(Context *context) { // Prime everything advance(context); advance(context); @@ -2851,511 +1920,6 @@ void parse_file(Context *context) parse_current(context); } -#define CHECK_EXPR(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) - -static Expr *parse_ternary_expr(Context *context, Expr *left_side) -{ - assert(expr_ok(left_side)); - Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side); - expr_ternary->ternary_expr.cond = left_side; - - // Check for elvis - if (try_consume(context, TOKEN_ELVIS)) - { - expr_ternary->ternary_expr.then_expr = NULL; - } - else - { - advance_and_verify(context, TOKEN_QUESTION); - Expr *true_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr); - expr_ternary->ternary_expr.then_expr = true_expr; - CONSUME_OR(TOKEN_COLON, &poisoned_expr); - } - - Expr *false_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr); - expr_ternary->ternary_expr.else_expr = false_expr; - return expr_ternary; -} - -static Expr *parse_unary_expr(Context *context, Expr *left) -{ - assert(!left && "Did not expect a left hand side!"); - - TokenType operator_type = context->tok.type; - - Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, context->tok); - unary->unary_expr.operator = unaryop_from_token(operator_type); - Precedence rule_precedence = rules[operator_type].precedence; - advance(context); - Expr *right_side = parse_precedence(context, rule_precedence); - - CHECK_EXPR(right_side); - - unary->unary_expr.expr = right_side; - return unary; -} - -static Expr *parse_post_unary(Context *context, Expr *left) -{ - assert(expr_ok(left)); - Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, context->tok); - unary->post_expr.expr = left; - unary->post_expr.operator = post_unaryop_from_token(context->tok.type); - advance(context); - return unary; -} - -/** - * grouping_expr - * : '(' expression ')' - * ; - */ -static Expr *parse_grouping_expr(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - advance_and_verify(context, TOKEN_LPAREN); - Expr *right = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); - return right; -} - - -static Expr *parse_binary(Context *context, Expr *left_side) -{ - assert(left_side && expr_ok(left_side)); - // Remember the operator. - TokenType operator_type = context->tok.type; - - advance(context); - - Expr *right_side; - if (context->tok.type == TOKEN_LBRACE && operator_type == TOKEN_EQ) - { - right_side = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); - } - else - { - right_side = TRY_EXPR_OR(parse_precedence(context, rules[operator_type].precedence + 1), &poisoned_expr); - } - - Expr *expr = EXPR_NEW_EXPR(EXPR_BINARY, left_side); - expr->binary_expr.operator = binaryop_from_token(operator_type); - expr->binary_expr.left = left_side; - expr->binary_expr.right = right_side; - return expr; -} - -static Expr *parse_call_expr(Context *context, Expr *left) -{ - assert(left && expr_ok(left)); - - advance_and_verify(context, TOKEN_LPAREN); - - Expr **params = NULL; - while (!try_consume(context, TOKEN_RPAREN)) - { - Expr *param = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); - params = VECADD(params, param); - COMMA_RPAREN_OR(&poisoned_expr); - } - Expr *call = EXPR_NEW_EXPR(EXPR_CALL, left); - call->call_expr.function = left; - call->call_expr.arguments = params; - return call; -} - - -static Expr *parse_subscript_expr(Context *context, Expr *left) -{ - assert(left && expr_ok(left)); - - advance_and_verify(context, TOKEN_LBRACKET); - Expr *index = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); - CONSUME_OR(TOKEN_RBRACKET, &poisoned_expr); - Expr *subscript_ast = EXPR_NEW_EXPR(EXPR_SUBSCRIPT, left); - subscript_ast->subscript_expr.expr = left; - subscript_ast->subscript_expr.index = index; - return subscript_ast; -} - - -static Expr *parse_access_expr(Context *context, Expr *left) -{ - assert(left && expr_ok(left)); - advance_and_verify(context, TOKEN_DOT); - Expr *access_expr = EXPR_NEW_EXPR(EXPR_ACCESS, left); - access_expr->access_expr.parent = left; - access_expr->access_expr.sub_element = context->tok; - TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", &poisoned_expr); - return access_expr; -} - -static int append_esc_string_token(char *restrict dest, const char *restrict src, size_t *pos) -{ - int scanned = 0; - uint64_t unicode_char = 0; - switch (src[0]) - { - case 'a': - dest[(*pos)++] = '\a'; - return 1; - case 'b': - dest[(*pos)++] = '\b'; - return 1; - case 'e': - dest[(*pos)++] = 0x1b; - return 1; - case 'f': - dest[(*pos)++] = '\f'; - return 1; - case 'n': - dest[(*pos)++] = '\n'; - return 1; - case 'r': - dest[(*pos)++] = '\r'; - return 1; - case 't': - dest[(*pos)++] = '\t'; - return 1; - case 'x': - { - int h = char_to_nibble(src[1]); - int l = char_to_nibble(src[2]); - if (h < 0 || l < 0) return -1; - unicode_char = ((unsigned) h << 4U) + l; - scanned = 3; - break; - } - case 'u': - { - int x1 = char_to_nibble(src[1]); - int x2 = char_to_nibble(src[2]); - int x3 = char_to_nibble(src[3]); - int x4 = char_to_nibble(src[4]); - if (x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0) return -1; - unicode_char = ((unsigned) x1 << 12U) + ((unsigned) x2 << 8U) + ((unsigned) x3 << 4U) + x4; - scanned = 5; - break; - } - case 'U': - { - int x1 = char_to_nibble(src[1]); - int x2 = char_to_nibble(src[2]); - int x3 = char_to_nibble(src[3]); - int x4 = char_to_nibble(src[4]); - int x5 = char_to_nibble(src[5]); - int x6 = char_to_nibble(src[6]); - int x7 = char_to_nibble(src[7]); - int x8 = char_to_nibble(src[8]); - if (x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || x5 < 0 || x6 < 0 || x7 < 0 || x8 < 0) return -1; - unicode_char = ((unsigned) x1 << 28U) + ((unsigned) x2 << 24U) + ((unsigned) x3 << 20U) + ((unsigned) x4 << 16U) + - ((unsigned) x5 << 12U) + ((unsigned) x6 << 8U) + ((unsigned) x7 << 4U) + x8; - scanned = 9; - break; - } - default: - dest[(*pos)++] = src[0]; - return 1; - } - if (unicode_char < 0x80U) - { - dest[(*pos)++] = (char)unicode_char; - } - else if (unicode_char < 0x800U) - { - dest[(*pos)++] = (char)(0xC0U | (unicode_char >> 6U)); - dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU)); - } - else if (unicode_char < 0x10000U) - { - dest[(*pos)++] = (char)(0xE0U | (unicode_char >> 12U)); - dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 6U) & 0x3FU)); - dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU)); - } - else - { - dest[(*pos)++] = (char)(0xF0U | (unicode_char >> 18U)); - dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 12U) & 0x3FU)); - dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 6U) & 0x3FU)); - dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU)); - } - return scanned; -} - -static Expr *parse_string_literal(Context *context, Expr *left) -{ - assert(!left && "Had left hand side"); - Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - expr_string->resolve_status = RESOLVE_DONE; - expr_string->type = type_string; - - char *str = NULL; - size_t len = 0; - - while (context->tok.type == TOKEN_STRING) - { - char *new_string = malloc_arena(len + source_range_len(context->tok.span)); - if (str) memcpy(new_string, str, len); - str = new_string; - for (unsigned i = 1; i < source_range_len(context->tok.span) - 1; i++) - { - if (context->tok.string[i] == '\\') - { - i++; - i += append_esc_string_token(str, context->tok.string + i, &len) - 1; - continue; - } - str[len++] = context->tok.string[i]; - } - advance_and_verify(context, TOKEN_STRING); - } - - assert(str); - str[len] = '\0'; - expr_string->const_expr.string.chars = str; - expr_string->const_expr.string.len = len; - expr_string->type = type_string; - expr_string->const_expr.type = CONST_STRING; - return expr_string; -} - - - -static Expr *parse_integer(Context *context, Expr *left) -{ - assert(!left && "Had left hand side"); - Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - const char *string = context->tok.start; - const char *end = string + source_range_len(context->tok.span); - uint64_t i = 0; - if (string[0] == '\'') - { - union - { - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - uint8_t b[8]; - } bytes; - int pos = 0; - while (++string < end - 1) - { - if (*string == '\\') - { - if (*(++string) == 'x') - { - int hex = 0; - for (int j = 0; j < 2; j++) - { - hex <<= 4U; - char c = *(++string); - if (c < 'A') - { - hex += c - '0'; - } - else if (c < 'a') - { - hex += c - 'A' + 10; - } - else - { - hex += c - 'a' + 10; - } - } - bytes.b[pos++] = hex; - continue; - } - } - bytes.b[pos++] = (unsigned)*string; - } - switch (pos) - { - case 1: - expr_int->const_expr.i = bytes.u8; - break; - case 2: - expr_int->const_expr.i = bytes.u16; - break; - case 4: - expr_int->const_expr.i = bytes.u32; - break; - case 8: - expr_int->const_expr.i = bytes.u64; - break; - } - expr_int->const_expr.type = CONST_INT; - expr_int->type = i > INT64_MAX ? type_compuint : type_compint; - expr_int->resolve_status = RESOLVE_DONE; - advance(context); - return expr_int; - } - switch (source_range_len(context->tok.span) > 2 ? string[1] : '0') - { - case 'x': - string += 2; - while (string < end) - { - char c = *(string++); - if (c == '_') continue; - if (i > (UINT64_MAX >> 4u)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i <<= 4u; - if (c < 'A') - { - i += c - '0'; - } - else if (c < 'a') - { - i += c - 'A' + 10; - } - else - { - i += c - 'a' + 10; - } - } - break; - case 'o': - string += 2; - while (string < end) - { - char c = *(string++); - if (c == '_') continue; - if (i > (UINT64_MAX >> 3U)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i <<= (unsigned) 3; - i += c - '0'; - } - break; - case 'b': - string += 2; - while (string < end) - { - char c = *(string++); - if (c == '_') continue; - if (i > (UINT64_MAX >> 1U)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i <<= (unsigned) 1; - i += c - '0'; - } - break; - default: - while (string < end) - { - char c = *(string++); - if (c == '_') continue; - if (i > (UINT64_MAX / 10)) - { - SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); - return &poisoned_expr; - } - i *= 10; - i += c - '0'; - } - break; - - } - expr_int->const_expr.i = i; - expr_int->const_expr.type = CONST_INT; - expr_int->type = i > INT64_MAX ? type_compuint : type_compint; - advance(context); - return expr_int; -} - - -static Expr *parse_double(Context *context, Expr *left) -{ - assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - char *end = NULL; - // IMPROVE - long double fval = strtold(context->tok.start, &end); - if (end != source_range_len(context->tok.span) + context->tok.start) - { - SEMA_TOKEN_ERROR(context->tok, "Invalid float value"); - return &poisoned_expr; - } - advance(context); - number->const_expr.f = fval; - number->type = type_compfloat; - number->const_expr.type = CONST_FLOAT; - return number; -} - -static Expr *parse_bool(Context *context, Expr *left) -{ - assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .type = CONST_BOOL }; - number->type = type_bool; - number->resolve_status = RESOLVE_DONE; - advance(context); - return number; -} - -static Expr *parse_nil(Context *context, Expr *left) -{ - assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - number->const_expr.type = CONST_NIL; - number->type = type_voidptr; - advance(context); - return number; -} - - - -/** - * initializer_list - * : '{' initializer_values '}' - * | '{' initializer_values ',' '}' - * ; - * - * initializer_values - * : initializer - * | initializer_values ',' initializer - * ; - * - * @param elements - * @return - */ -static Expr *parse_initializer_list(Context *context) -{ - Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok); - CONSUME_OR(TOKEN_LBRACE, &poisoned_expr); - while (!try_consume(context, TOKEN_RBRACE)) - { - Expr *expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_expr); - initializer_list->initializer_expr = VECADD(initializer_list->initializer_expr, expr); - if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RBRACE) - { - SEMA_TOKEN_ERROR(context->tok, "Expected ',' or '}'"); - return &poisoned_expr; - } - } - return initializer_list; -} - -static Expr *parse_initializer(Context *context) -{ - if (context->tok.type == TOKEN_LBRACE) - { - return parse_initializer_list(context); - } - else - { - return parse_expr(context); - } -} /** @@ -3388,22 +1952,6 @@ static Expr *parse_type_access(Context *context, TypeInfo *type) } -static Expr *parse_identifier_with_path(Context *context, Path *path) -{ - Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, context->tok); - expr->identifier_expr.identifier = context->tok.string; - expr->identifier_expr.path = path; - advance(context); - return expr; -} - -static Expr *parse_identifier(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - return parse_identifier_with_path(context, NULL); -} - - /** * type_identifier * : TYPE_IDENT initializer_list @@ -3413,7 +1961,7 @@ static Expr *parse_identifier(Context *context, Expr *left) * @param left must be null. * @return Expr* */ -static Expr *parse_type_identifier_with_path(Context *context, Path *path) +Expr *parse_type_identifier_with_path(Context *context, Path *path) { TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER); type->unresolved.path = path; @@ -3430,140 +1978,9 @@ static Expr *parse_type_identifier_with_path(Context *context, Path *path) return parse_type_access(context, type); } -/** - * @param left must be null. - * @return Expr* - */ -static Expr *parse_type_identifier(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - return parse_type_identifier_with_path(context, NULL); -} - -static Expr *parse_maybe_scope(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - Path *path = parse_path_prefix(context); - switch (context->tok.type) - { - case TOKEN_IDENT: - case TOKEN_CT_IDENT: - case TOKEN_CONST_IDENT: - return parse_identifier_with_path(context, path); - case TOKEN_TYPE_IDENT: - return parse_type_identifier_with_path(context, path); - default: - SEMA_TOKEN_ERROR(context->tok, "Expected a type, function or constant."); - return &poisoned_expr; - } -} -static Expr *parse_type_expr(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE, context->tok); - advance_and_verify(context, TOKEN_TYPE); - CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); - expr->type_expr.type = type; - return expr; -} -static Expr *parse_try_expr(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok); - advance_and_verify(context, TOKEN_TRY); - try_expr->try_expr.expr = TRY_EXPR_OR(parse_precedence(context, PREC_TRY + 1), &poisoned_expr); - if (try_consume(context, TOKEN_ELSE)) - { - try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr); - } - return try_expr; -} -static Expr *parse_macro_expr(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - Expr *macro_expr = EXPR_NEW_TOKEN(EXPR_MACRO_EXPR, context->tok); - advance_and_verify(context, TOKEN_AT); - macro_expr->macro_expr = TRY_EXPR_OR(parse_precedence(context, PREC_UNARY + 1), &poisoned_expr); - return macro_expr; -} -static Expr *parse_cast_expr(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_CAST, context->tok); - advance_and_verify(context, TOKEN_CAST); - CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); - expr->cast_expr.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr); - CONSUME_OR(TOKEN_COMMA, &poisoned_expr); - expr->cast_expr.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); - return expr; -} -ParseRule rules[TOKEN_EOF + 1] = { - [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, - [TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY }, - [TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, - [TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, - [TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL }, - [TOKEN_TYPE] = { parse_type_expr, NULL, PREC_NONE }, - [TOKEN_CAST] = { parse_cast_expr, NULL, PREC_NONE }, - [TOKEN_TRY] = { parse_try_expr, NULL, PREC_TRY }, - //[TOKEN_SIZEOF] = { parse_sizeof, NULL, PREC_NONE }, - [TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL }, - [TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, - [TOKEN_MINUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE }, - [TOKEN_PLUS] = { NULL, parse_binary, PREC_ADDITIVE }, - [TOKEN_PLUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE }, - [TOKEN_DIV] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, - [TOKEN_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, - [TOKEN_STAR] = { parse_unary_expr, parse_binary, PREC_MULTIPLICATIVE }, - [TOKEN_MULT_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, - [TOKEN_DOT] = { NULL, parse_access_expr, PREC_CALL }, - [TOKEN_NOT] = { parse_unary_expr, NULL, PREC_UNARY }, - [TOKEN_BIT_NOT] = { parse_unary_expr, NULL, PREC_UNARY }, - [TOKEN_BIT_XOR] = { NULL, parse_binary, PREC_BIT }, - [TOKEN_BIT_OR] = { NULL, parse_binary, PREC_BIT }, - [TOKEN_AMP] = { parse_unary_expr, parse_binary, PREC_BIT }, - [TOKEN_EQEQ] = { NULL, parse_binary, PREC_RELATIONAL }, - [TOKEN_NOT_EQUAL] = { NULL, parse_binary, PREC_RELATIONAL }, - [TOKEN_GREATER] = { NULL, parse_binary, PREC_RELATIONAL }, - [TOKEN_GREATER_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, - [TOKEN_LESS] = { NULL, parse_binary, PREC_RELATIONAL }, - [TOKEN_LESS_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, - [TOKEN_SHL] = { NULL, parse_binary, PREC_SHIFT }, - [TOKEN_SHR] = { NULL, parse_binary, PREC_SHIFT }, - [TOKEN_TRUE] = { parse_bool, NULL, PREC_NONE }, - [TOKEN_FALSE] = { parse_bool, NULL, PREC_NONE }, - [TOKEN_NIL] = { parse_nil, NULL, PREC_NONE }, - [TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE }, - [TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE }, - [TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, - [TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE }, - [TOKEN_AT] = { parse_macro_expr, NULL, PREC_UNARY }, - [TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, - [TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE }, - [TOKEN_FLOAT] = { parse_double, NULL, PREC_NONE }, - [TOKEN_OR] = { NULL, parse_binary, PREC_LOGICAL }, - [TOKEN_AND] = { NULL, parse_binary, PREC_LOGICAL }, - [TOKEN_EQ] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_PLUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_PLUS_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_MINUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_MINUS_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_MULT_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_MULT_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_DIV_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_BIT_XOR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_BIT_AND_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_BIT_OR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_SHR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_SHL_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, -}; diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h new file mode 100644 index 000000000..ea1e2ec06 --- /dev/null +++ b/src/compiler/parser_internal.h @@ -0,0 +1,85 @@ +#pragma once + +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(context, _name)) return _res; } while(0) +#define EXPECT_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; } while(0) +#define CONSUME_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; advance(context); } while(0) +#define TRY_EXPECT_OR(_tok, _message, _type) do { if (context->tok.type != _tok) { SEMA_TOKEN_ERROR(context->tok, _message); return _type; } } while(0) +#define TRY_CONSUME_OR(_tok, _message, _type) do { if (!consume(context, _tok, _message)) return _type; } while(0) +#define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR(_tok, _message, &poisoned_ast) +#define TRY_CONSUME_EOS_OR(_res) TRY_CONSUME_OR(TOKEN_EOS, "Expected ';'", _res) +#define TRY_CONSUME_EOS() TRY_CONSUME_EOS_OR(&poisoned_ast) +#define RETURN_AFTER_EOS(_ast) extend_ast_with_prev_token(context, ast); TRY_CONSUME_EOS_OR(&poisoned_ast); return _ast +#define TRY_CONSUME_LBRACE() TRY_CONSUME(TOKEN_LBRACE, "Expected '{'") + +#define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; }) +#define TRY_AST(_ast_stmt) TRY_AST_OR(_ast_stmt, &poisoned_ast) +#define TRY_EXPR_OR(_expr_stmt, _res) ({ Expr* _expr = (_expr_stmt); if (!expr_ok(_expr)) return _res; _expr; }) +#define TRY_TYPE_OR(_type_stmt, _res) ({ TypeInfo* _type = (_type_stmt); if (!type_info_ok(_type)) return _res; _type; }) +#define TRY_DECL_OR(_decl_stmt, _res) ({ Decl* _decl = (_decl_stmt); if (!decl_ok(_decl)) return _res; _decl; }) + +#define CHECK_EXPR(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) + + +#define COMMA_RPAREN_OR(_res) \ +do { if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RPAREN) { \ +SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0) + + +Ast *parse_stmt(Context *context); +Path *parse_path_prefix(Context *context); +Expr *parse_type_identifier_with_path(Context *context, Path *path); +Expr *parse_expr(Context *context); +TypeInfo *parse_type_expression(Context *context); +Expr* parse_constant_expr(Context *context); +Expr *parse_initializer_list(Context *context); +Expr *parse_initializer(Context *context); +Decl *parse_decl(Context *context); +Ast *parse_decl_expr_list(Context *context); +Ast* parse_compound_stmt(Context *context); +Expr *parse_expression_list(Context *context); +bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr); +Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type); +bool parse_param_list(Context *context, Expr ***result, bool allow_type); + +void error_at_current(Context *context, const char* message, ...); +bool try_consume(Context *context, TokenType type); +bool consume(Context *context, TokenType type, const char *message, ...); +bool consume_const_name(Context *context, const char* type); +Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence); + + +static inline bool expect(Context *context, TokenType token_type) +{ + if (token_type == context->tok.type) return true; + + SEMA_TOKEN_ERROR(context->tok, "Expected '%s'.", token_type_to_string(token_type)); + return false; +} + +static inline bool expect_ident(Context *context, const char* name) +{ + switch (context->tok.type) + { + case TOKEN_IDENT: + return true; + case TOKEN_TYPE_IDENT: + case TOKEN_CONST_IDENT: + SEMA_TOKEN_ERROR(context->tok, "A %s cannot start with a capital letter.", name); + return false; + default: + SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name); + return false; + } +} + +static inline Expr *parse_paren_expr(Context *context) +{ + CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); + Expr *expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); + CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); + return expr; +} diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c new file mode 100644 index 000000000..b7401b142 --- /dev/null +++ b/src/compiler/sema_decls.c @@ -0,0 +1,458 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "sema_internal.h" + + +static inline bool sema_analyse_error(Context *context __unused, Decl *decl) +{ + Decl **constants = decl->error.error_constants; + unsigned size = vec_size(constants); + if (size > MAX_ERRORS) + { + SEMA_ERROR(decl, "More than %d errors declared in a single error type.", MAX_ERRORS); + return false; + } + bool success = true; + for (unsigned i = 0; i < size; i++) + { + Decl *constant = constants[i]; + for (unsigned j = 0; j < i; j++) + { + if (constant->name == constants[j]->name) + { + SEMA_ERROR(constant, "Duplicate error names, please remove one of them."); + SEMA_PREV(constants[j], "The previous declaration was here."); + decl_poison(constant); + decl_poison(constants[j]); + success = false; + break; + } + } + constant->error_constant.value = i; + constant->resolve_status = RESOLVE_DONE; + } + return success; +} + + +static inline bool sema_analyse_struct_member(Context *context, Decl *decl) +{ + if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION) + { + DEBUG_LOG("Beginning analysis of inner struct/union"); + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + if (!decl_ok(member)) + { + decl_poison(decl); + continue; + } + if (!sema_analyse_struct_member(context, decl->strukt.members[i])) + { + if (decl_ok(decl)) + { + decl_poison(decl); + continue; + } + decl_poison(decl); + } + } + DEBUG_LOG("Analysis complete."); + return decl_ok(decl); + } + assert(decl->decl_kind == DECL_VAR); + assert(decl->var.kind == VARDECL_MEMBER); + assert(!decl->var.init_expr); + if (!sema_resolve_type_info(context, decl->var.type_info)) + { + decl_poison(decl); + return false; + } + decl->type = decl->var.type_info->type; + assert(decl->var.type_info->type); + return true; +} + +static inline bool sema_analyse_struct_union(Context *context, Decl *decl) +{ + DEBUG_LOG("Beginning analysis of %s.", decl->name); + assert(decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION); + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + if (!decl_ok(member)) + { + decl_poison(decl); + continue; + } + if (!sema_analyse_struct_member(context, decl->strukt.members[i])) + { + if (decl_ok(decl)) + { + decl_poison(decl); + continue; + } + decl_poison(decl); + } + } + DEBUG_LOG("Analysis complete."); + return decl_ok(decl); +} + + +static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function) +{ + assert(param->decl_kind == DECL_VAR); + assert(param->var.kind == VARDECL_PARAM); + if (!sema_resolve_type_info(context, param->var.type_info)) + { + return false; + } + param->type = param->var.type_info->type; + if (param->var.init_expr && !is_function) + { + SEMA_ERROR(param->var.init_expr, "Function types may not have default arguments."); + return false; + } + if (param->var.init_expr) + { + Expr *expr = param->var.init_expr; + if (!sema_analyse_expr(context, param->type, expr)) return false; + if (expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(expr, "Only constant expressions may be used as default values."); + return false; + } + } + return true; +} + +static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function) +{ + char buffer[MAX_FUNCTION_SIGNATURE_SIZE + 200]; + size_t buffer_write_offset = 0; + bool all_ok = true; + all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok; + if (all_ok) + { + type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset); + buffer[buffer_write_offset++] = '('; + } + if (vec_size(signature->params) > MAX_PARAMS) + { + SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS); + return false; + } + STable *names = &context->scratch_table; + stable_clear(names); + + VECEACH(signature->params, i) + { + Decl *param = signature->params[i]; + assert(param->resolve_status == RESOLVE_NOT_DONE); + param->resolve_status = RESOLVE_RUNNING; + if (!sema_analyse_function_param(context, param, is_function)) + { + decl_poison(param); + all_ok = false; + continue; + } + param->resolve_status = RESOLVE_DONE; + if (i > 0 && all_ok) + { + buffer[buffer_write_offset++] = ','; + } + type_append_signature_name(param->var.type_info->type, buffer, &buffer_write_offset); + if (param->name) + { + Decl *prev = stable_set(names, param->name, param); + if (prev) + { + SEMA_ERROR(param, "Duplicate parameter name %s.", param->name); + SEMA_PREV(prev, "Previous use of the name was here."); + decl_poison(prev); + decl_poison(param); + all_ok = false; + } + } + } + if (signature->variadic) + { + buffer[buffer_write_offset++] = ','; + buffer[buffer_write_offset++] = '.'; + buffer[buffer_write_offset++] = '.'; + buffer[buffer_write_offset++] = '.'; + } + buffer[buffer_write_offset++] = ')'; + if (signature->throw_any) + { + assert(!signature->throws); + buffer[buffer_write_offset++] = '!'; + } + if (vec_size(signature->throws)) + { + buffer[buffer_write_offset++] = '!'; + VECEACH(signature->throws, i) + { + Decl *err_decl = signature->throws[i]; + if (!sema_analyse_decl(context, err_decl)) + { + continue; + } + if (i > 0 && all_ok) + { + buffer[buffer_write_offset++] = '|'; + } + type_append_signature_name(err_decl->type, buffer, &buffer_write_offset); + } + } + if (!all_ok) return NULL; + TokenType type = TOKEN_INVALID_TOKEN; + signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type); + Type *func_type = stable_get(&context->local_symbols, signature->mangled_signature); + if (!func_type) + { + func_type = type_new(TYPE_FUNC, signature->mangled_signature); + func_type->canonical = func_type; + func_type->func.signature = signature; + stable_set(&context->local_symbols, signature->mangled_signature, func_type); + } + return func_type; + +} + +static inline bool sema_analyse_typedef(Context *context, Decl *decl) +{ + if (decl->typedef_decl.is_func) + { + Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false); + if (!func_type) return false; + decl->type->canonical = func_type; + return true; + } + if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false; + decl->type->canonical = decl->typedef_decl.type_info->type; + // Do we need anything else? + return true; +} + +static inline bool sema_analyse_enum(Context *context, Decl *decl) +{ + if (!sema_resolve_type_info(context, decl->enums.type_info)) return false; + uint64_t value = 0; + Type *type = decl->enums.type_info->type; + bool success = true; + VECEACH(decl->enums.values, i) + { + Decl *enum_value = decl->enums.values[i]; + enum_value->enum_constant.ordinal = i; + assert(enum_value->resolve_status == RESOLVE_NOT_DONE); + assert(enum_value->decl_kind == DECL_ENUM_CONSTANT); + enum_value->resolve_status = RESOLVE_RUNNING; + Expr *expr = enum_value->enum_constant.expr; + if (!expr) + { + expr = expr_new(EXPR_CONST, INVALID_RANGE); + expr->type = type; + expr->resolve_status = RESOLVE_DONE; + expr->const_expr.type = CONST_INT; + expr->const_expr.i = value; + enum_value->enum_constant.expr = expr; + } + if (!sema_analyse_expr(context, type, expr)) + { + success = false; + enum_value->resolve_status = RESOLVE_DONE; + continue; + } + assert(type_is_integer(expr->type->canonical)); + if (expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(expr, "Expected a constant expression for enum"); + success = false; + } + enum_value->resolve_status = RESOLVE_DONE; + enum_value->type = decl->type; + } + return success; +} + +static inline bool sema_analyse_throws(Context *context, Decl *decl) +{ + if (!sema_resolve_type_info(context, decl->throws)) return false; + decl->type = decl->throws->type; + return true; +} + + +static inline bool sema_analyse_method_function(Context *context, Decl *decl) +{ + TypeInfo *parent_type = decl->func.type_parent; + if (!sema_resolve_type_info(context, parent_type)) return false; + if (!type_may_have_method_functions(parent_type->type)) + { + SEMA_ERROR(decl, + "Method functions can not be associated with '%s'", + type_to_error_string(decl->func.type_parent->type)); + return false; + } + Decl *parent = parent_type->type->decl; + VECEACH(parent->method_functions, i) + { + Decl *function = parent->method_functions[i]; + if (function->name == decl->name) + { + SEMA_ERROR(decl, "Duplicate name '%s' for method function.", function->name); + SEMA_PREV(function, "Previous definition here."); + return false; + } + } + DEBUG_LOG("Method function '%s.%s' analysed.", parent->name, decl->name); + vec_add(parent->method_functions, decl); + return true; +} + + + +static inline bool sema_analyse_func(Context *context, Decl *decl) +{ + DEBUG_LOG("----Analysing function %s", decl->name); + Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true); + decl->type = func_type; + if (!func_type) return decl_poison(decl); + if (decl->func.type_parent) + { + if (!sema_analyse_method_function(context, decl)) return decl_poison(decl); + } + if (decl->func.body && !sema_analyse_function_body(context, decl)) return decl_poison(decl); + if (decl->name == main_name) + { + if (decl->visibility == VISIBLE_LOCAL) + { + SEMA_ERROR(decl, "'main' cannot have local visibility."); + return false; + } + decl->visibility = VISIBLE_EXTERN; + } + DEBUG_LOG("Function analysis done."); + return true; +} + +static inline bool sema_analyse_macro(Context *context, Decl *decl) +{ + TypeInfo *rtype = decl->macro_decl.rtype; + if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return false; + VECEACH(decl->macro_decl.parameters, i) + { + Decl *param = decl->macro_decl.parameters[i]; + assert(param->decl_kind == DECL_VAR); + assert(param->var.kind == VARDECL_PARAM); + if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false; + } + return true; +} + + + + +static inline bool sema_analyse_global(Context *context, Decl *decl) +{ + if (!sema_resolve_type_info(context, decl->var.type_info)) return false; + if (decl->var.init_expr) + { + if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return false; + if (decl->var.init_expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); + return false; + } + } + switch (decl->var.kind) + { + case VARDECL_CONST: + assert(decl->var.init_expr); + return true; + case VARDECL_GLOBAL: + return true; + default: + UNREACHABLE + break; + } +} + +static inline bool sema_analyse_generic(Context *context, Decl *decl) +{ + TODO + return true; +} + +bool sema_analyse_decl(Context *context, Decl *decl) +{ + if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); + + DEBUG_LOG("Analyse %s", decl->name); + if (decl->resolve_status == RESOLVE_RUNNING) + { + SEMA_ERROR(decl, "Recursive dependency on %s.", decl->name); + decl_poison(decl); + return false; + } + + decl->resolve_status = RESOLVE_RUNNING; + decl->module = context->module; + switch (decl->decl_kind) + { + case DECL_THROWS: + if (!sema_analyse_throws(context, decl)) return decl_poison(decl); + break; + case DECL_STRUCT: + case DECL_UNION: + if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); + llvm_set_struct_size_alignment(decl); + decl_set_external_name(decl); + break; + case DECL_FUNC: + if (!sema_analyse_func(context, decl)) return decl_poison(decl); + decl_set_external_name(decl); + break; + case DECL_MACRO: + if (!sema_analyse_macro(context, decl)) return decl_poison(decl); + break; + case DECL_VAR: + if (!sema_analyse_global(context, decl)) return decl_poison(decl); + decl_set_external_name(decl); + break; + case DECL_TYPEDEF: + if (!sema_analyse_typedef(context, decl)) return decl_poison(decl); + break; + case DECL_ENUM: + if (!sema_analyse_enum(context, decl)) return decl_poison(decl); + decl_set_external_name(decl); + break; + case DECL_ERROR: + if (!sema_analyse_error(context, decl)) return decl_poison(decl); + decl_set_external_name(decl); + break; + case DECL_GENERIC: + if (!sema_analyse_generic(context, decl)) return decl_poison(decl); + break; + case DECL_ATTRIBUTE: + TODO + case DECL_POISONED: + case DECL_IMPORT: + case DECL_ENUM_CONSTANT: + case DECL_ERROR_CONSTANT: + case DECL_ARRAY_VALUE: + case DECL_CT_ELSE: + case DECL_CT_ELIF: + UNREACHABLE + case DECL_CT_IF: + // Handled elsewhere + UNREACHABLE + } + decl->resolve_status = RESOLVE_DONE; + return true; +} diff --git a/src/compiler/expr_analysis.c b/src/compiler/sema_expr.c similarity index 93% rename from src/compiler/expr_analysis.c rename to src/compiler/sema_expr.c index 9348f29b7..9d5bb7619 100644 --- a/src/compiler/expr_analysis.c +++ b/src/compiler/sema_expr.c @@ -2,8 +2,14 @@ // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. -#include "compiler_internal.h" +#include "sema_internal.h" +/* + * TODOs + * - Check all returns correctly + * - Disallow jumping in and out of an expression block. + * - Handle IXX FXX and UXX in a sane way. + */ #define CONST_PROCESS(_op) \ if (both_const(left, right)) { \ @@ -15,6 +21,17 @@ expr->expr_kind = EXPR_CONST; \ expr->const_expr.type = left->const_expr.type; \ } +int sema_check_comp_time_bool(Context *context, Expr *expr) +{ + if (!sema_analyse_expr(context, type_bool, expr)) return -1; + if (expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(expr, "$if requires a compile time constant value."); + return -1; + } + return expr->const_expr.b; +} + static bool expr_is_ltype(Expr *expr) { switch (expr->expr_kind) @@ -34,6 +51,7 @@ static bool expr_is_ltype(Expr *expr) } } + static inline bool sema_type_error_on_binop(Expr *expr) { const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator)); @@ -1590,6 +1608,17 @@ static TypeInfo *type_info_copy_from_macro(Context *context, Expr *macro, TypeIn } +static void ast_copy_list_from_macro(Context *context, Expr *macro, Ast ***to_convert) +{ + Ast **result = NULL; + Ast **list = *to_convert; + VECEACH(list, i) + { + vec_add(result, ast_copy_from_macro(context, macro, list[i])); + } + *to_convert = result; +} + static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr) { @@ -1598,6 +1627,9 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex Expr *expr = expr_shallow_copy(source_expr); switch (source_expr->expr_kind) { + case EXPR_EXPR_BLOCK: + ast_copy_list_from_macro(context, macro, &expr->expr_block.stmts); + return expr; case EXPR_POISONED: return source_expr; case EXPR_TRY: @@ -1681,16 +1713,6 @@ static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr return result; } -static void ast_copy_list_from_macro(Context *context, Expr *macro, Ast ***to_convert) -{ - Ast **result = NULL; - Ast **list = *to_convert; - VECEACH(list, i) - { - vec_add(result, ast_copy_from_macro(context, macro, list[i])); - } - *to_convert = result; -} static void type_info_copy_list_from_macro(Context *context, Expr *macro, TypeInfo ***to_convert) { @@ -1782,9 +1804,6 @@ static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source) AST_COPY(ast->for_stmt.body); AST_COPY(ast->for_stmt.init); return ast; - case AST_FUNCTION_BLOCK_STMT: - ast_copy_list_from_macro(context, macro, &ast->function_block_stmt.stmts); - return ast; case AST_GENERIC_CASE_STMT: AST_COPY(ast->generic_case_stmt.body); // ast->generic_case_stmt.types = ... @@ -1934,8 +1953,130 @@ static inline bool sema_expr_analyse_type(Context *context, Type *to, Expr *expr +static inline Ast **context_push_returns(Context *context) +{ + Ast** old_returns = context->returns; + if (context->returns_cache) + { + context->returns = context->returns_cache; + context->returns_cache = NULL; + vec_resize(context->returns, 0); + } + else + { + context->returns = NULL; + } + return old_returns; +} + +static inline void context_pop_returns(Context *context, Ast **restore) +{ + if (!context->returns_cache && context->returns) + { + context->returns_cache = context->returns; + } + context->returns = restore; +} +static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr *expr) +{ + bool success = true; + expr->type = type_void; + Type *prev_expected_block_type = context->expected_block_type; + Ast **saved_returns = context_push_returns(context); + context->expected_block_type = to; + context_push_scope_with_flags(context, SCOPE_EXPR_BLOCK); + + VECEACH(expr->expr_block.stmts, i) + { + if (!sema_analyse_statement(context, expr->expr_block.stmts[i])) + { + success = false; + goto EXIT; + } + } + + if (!vec_size(context->returns)) + { + if (to) + { + SEMA_ERROR(expr, "Expected at least one return out of expression block to return a value of type %s.", type_to_error_string(to)); + success = false; + } + goto EXIT; + } + + Expr *first_return_expr = context->returns[0]->return_stmt.expr; + Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; + bool all_returns_needs_casts = false; + // Let's unify the return statements. + VECEACH(context->returns, i) + { + Ast *return_stmt = context->returns[i]; + Expr *ret_expr = return_stmt->return_stmt.expr; + bool last_expr_was_void = left_canonical == type_void; + Type *right_canonical = ret_expr ? ret_expr->type->canonical : type_void; + bool current_expr_was_void = right_canonical == type_void; + if (i > 0 && last_expr_was_void != current_expr_was_void) + { + SEMA_ERROR(return_stmt, "You can't combine empty returns with value returns."); + SEMA_PREV(context->returns[i - 1], "Previous return was here."); + success = false; + goto EXIT; + } + if (to) + { + if (current_expr_was_void) + { + SEMA_ERROR(return_stmt, "The return must be a value of type '%s'.", type_to_error_string(to)); + success = false; + goto EXIT; + } + if (!cast_implicit(ret_expr, to)) + { + success = false; + goto EXIT; + } + continue; + } + + // The simple case. + if (left_canonical == right_canonical) continue; + + // Try to find a common type: + Type *max = type_find_max_type(left_canonical, right_canonical); + if (!max) + { + SEMA_ERROR(return_stmt, "Cannot find a common parent type of '%s' and '%s'", + type_to_error_string(left_canonical), type_to_error_string(right_canonical)); + SEMA_PREV(context->returns[i - 1], "The previous return was here."); + success = false; + goto EXIT; + } + left_canonical = max; + all_returns_needs_casts = true; + } + if (all_returns_needs_casts) + { + VECEACH(context->returns, i) + { + Ast *return_stmt = context->returns[i]; + Expr *ret_expr = return_stmt->return_stmt.expr; + if (!cast_implicit(ret_expr, left_canonical)) + { + success = false; + goto EXIT; + } + } + } + expr->type = left_canonical; +EXIT: + context_pop_scope(context); + context_pop_returns(context, saved_returns); + context->expected_block_type = prev_expected_block_type; + return success; +} static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr) { @@ -1945,6 +2086,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return false; case EXPR_SCOPED_EXPR: UNREACHABLE + case EXPR_EXPR_BLOCK: + return sema_expr_analyse_expr_block(context, to, expr); case EXPR_MACRO_EXPR: return sema_expr_analyse_macro_expr(context, to, expr); case EXPR_TRY: diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h new file mode 100644 index 000000000..5fc29737e --- /dev/null +++ b/src/compiler/sema_internal.h @@ -0,0 +1,13 @@ +#pragma once + +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + + +#include "compiler_internal.h" + +int sema_check_comp_time_bool(Context *context, Expr *expr); +bool sema_analyse_function_body(Context *context, Decl *func); +void context_pop_scope(Context *context); +void context_push_scope_with_flags(Context *context, ScopeFlags flags); diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c new file mode 100644 index 000000000..91d39fa5a --- /dev/null +++ b/src/compiler/sema_passes.c @@ -0,0 +1,123 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "sema_internal.h" + +void sema_analysis_pass_process_imports(Context *context) +{ + DEBUG_LOG("Pass: Importing dependencies for %s", context->file->name); + unsigned imports = vec_size(context->imports); + for (unsigned i = 0; i < imports; i++) + { + Decl *import = context->imports[i]; + import->resolve_status = RESOLVE_RUNNING; + Path *path = import->import.path; + Module *module = stable_get(&compiler.modules, path->module); + DEBUG_LOG("- Import of %s.", path->module); + if (!module) + { + SEMA_ERROR(import, "No module named '%s' could be found.", path->module); + decl_poison(import); + continue; + } + import->module = module; + for (unsigned j = 0; j < i; j++) + { + if (import->module == context->imports[j]->module) + { + SEMA_ERROR(import, "Module '%s' imported more than once.", path->module); + SEMA_PREV(context->imports[i], "Previous import was here"); + decl_poison(import); + break; + } + } + } + DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors); +} + + +static inline void sema_append_decls(Context *context, Decl **decls) +{ + VECEACH(decls, i) + { + context_register_global_decl(context, decls[i]); + } +} + +static inline bool sema_analyse_top_level_if(Context *context, Decl *ct_if) +{ + int res = sema_check_comp_time_bool(context, ct_if->ct_if_decl.expr); + if (res == -1) return false; + if (res) + { + // Append declarations + sema_append_decls(context, ct_if->ct_if_decl.then); + return true; + } + + // False, so check elifs + Decl *ct_elif = ct_if->ct_if_decl.elif; + while (ct_elif) + { + if (ct_elif->decl_kind == DECL_CT_ELIF) + { + res = sema_check_comp_time_bool(context, ct_elif->ct_elif_decl.expr); + if (res == -1) return false; + if (res) + { + sema_append_decls(context, ct_elif->ct_elif_decl.then); + return true; + } + ct_elif = ct_elif->ct_elif_decl.elif; + } + else + { + assert(ct_elif->decl_kind == DECL_CT_ELSE); + sema_append_decls(context, ct_elif->ct_elif_decl.then); + return true; + } + } + return true; +} + + +void sema_analysis_pass_conditional_compilation(Context *context) +{ + DEBUG_LOG("Pass: Top level conditionals %s", context->file->name); + for (unsigned i = 0; i < vec_size(context->ct_ifs); i++) + { + sema_analyse_top_level_if(context, context->ct_ifs[i]); + } + DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors); +} + +void sema_analysis_pass_decls(Context *context) +{ + DEBUG_LOG("Pass: Decl analysis %s", context->file->name); + VECEACH(context->enums, i) + { + sema_analyse_decl(context, context->enums[i]); + } + VECEACH(context->types, i) + { + sema_analyse_decl(context, context->types[i]); + } + VECEACH(context->error_types, i) + { + sema_analyse_decl(context, context->error_types[i]); + } + VECEACH(context->struct_functions, i) + { + sema_analyse_decl(context, context->struct_functions[i]); + } + VECEACH(context->vars, i) + { + sema_analyse_decl(context, context->vars[i]); + } + VECEACH(context->functions, i) + { + sema_analyse_decl(context, context->functions[i]); + } + DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors); +} diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c new file mode 100644 index 000000000..650f53a81 --- /dev/null +++ b/src/compiler/sema_stmts.c @@ -0,0 +1,921 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "sema_internal.h" + +#pragma mark --- Context help functions + +void context_push_scope_with_flags(Context *context, ScopeFlags flags) +{ + if (context->current_scope == &context->scopes[MAX_SCOPE_DEPTH - 1]) + { + FATAL_ERROR("Too deeply nested scopes."); + } + ScopeFlags previous_flags = context->current_scope->flags; + Ast *parent_defer = context->current_scope->defers.start; + context->current_scope++; + context->current_scope->exit = EXIT_NONE; + context->current_scope->local_decl_start = context->last_local; + context->current_scope->defers.start = parent_defer; + context->current_scope->defers.end = parent_defer; + if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK)) + { + context->current_scope->flags = flags; + } + else + { + context->current_scope->flags = previous_flags | flags; + } + context->current_scope->flags_created = flags; +} + +static inline void context_push_scope(Context *context) +{ + context_push_scope_with_flags(context, SCOPE_NONE); +} + +static inline void context_pop_defers(Context *context) +{ + context->current_scope->defers.start = context->current_scope->defers.end; +} + +static inline void context_pop_defers_to(Context *context, DeferList *list) +{ + *list = context->current_scope->defers; + context_pop_defers(context); +} + +static inline void context_add_exit(Context *context, ExitType exit) +{ + if (context->current_scope->exit < exit) + { + context->current_scope->exit = exit; + } +} + +void context_pop_scope(Context *context) +{ + assert(context->current_scope != &context->scopes[0]); + context->last_local = context->current_scope->local_decl_start; + ExitType exit_type = context->current_scope->exit; + assert (context->current_scope->defers.end == context->current_scope->defers.start); + context->current_scope--; + if (context->current_scope->exit < exit_type) + { + context->current_scope->exit = exit_type; + } +} + +static Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr) +{ + DeferList defers = { NULL, NULL }; + context_pop_defers_to(context, &defers); + if (defers.end == defers.start) return expr; + Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span); + wrap->type = expr->type; + wrap->resolve_status = RESOLVE_DONE; + wrap->expr_scope.expr = expr; + wrap->expr_scope.defers = defers; + return expr; +} + +static void context_pop_defers_and_replace_ast(Context *context, Ast *ast) +{ + DeferList defers = { NULL, NULL }; + context_pop_defers_to(context, &defers); + if (defers.end == defers.start) return; + if (ast->ast_kind == AST_DEFER_STMT) + { + assert(defers.start == ast); + *ast = *ast->defer_stmt.body; + return; + } + assert(ast->ast_kind != AST_COMPOUND_STMT); + Ast *replacement = malloc_arena(sizeof(Ast)); + *replacement = *ast; + ast->ast_kind = AST_SCOPED_STMT; + ast->scoped_stmt.stmt = replacement; + ast->scoped_stmt.defers = defers; +} + +#pragma mark --- Helper functions + +#define UPDATE_EXIT(exit_type) \ + do { if (!context->current_scope->exit) context->current_scope->exit = exit_type; } while(0) + +#pragma mark --- Sema analyse stmts + + +static inline bool sema_analyse_block_return_stmt(Context *context, Ast *statement) +{ + assert(context->current_scope->flags & SCOPE_EXPR_BLOCK); + UPDATE_EXIT(EXIT_RETURN); + if (statement->return_stmt.expr && !sema_analyse_expr(context, context->expected_block_type, statement->return_stmt.expr)) return false; + vec_add(context->returns, statement); + return true; +} + +static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) +{ + // This might be a return in a function block or a macro which must be treated differently. + if (context->current_scope->flags & SCOPE_EXPR_BLOCK) + { + return sema_analyse_block_return_stmt(context, statement); + } + + UPDATE_EXIT(EXIT_RETURN); + context->current_scope->exit = EXIT_RETURN; + + Type *expected_rtype = context->rtype; + Expr *return_expr = statement->return_stmt.expr; + statement->return_stmt.defer = context->current_scope->defers.start; + if (return_expr == NULL) + { + if (!expected_rtype) + { + assert(context->evaluating_macro); + context->rtype = type_void; + return true; + } + if (expected_rtype->canonical != type_void) + { + SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype)); + return false; + } + return true; + } + if (!sema_analyse_expr(context, expected_rtype, return_expr)) return false; + if (!expected_rtype) + { + assert(context->evaluating_macro); + context->rtype = type_void; + context->active_function_for_analysis->func.function_signature.rtype->type->canonical = statement->return_stmt.expr->type->canonical; + return true; + } + assert(statement->return_stmt.expr->type->canonical == expected_rtype->canonical); + return true; +} + + +static inline bool sema_analyse_var_decl(Context *context, Decl *decl) +{ + assert(decl->decl_kind == DECL_VAR); + if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl); + decl->type = decl->var.type_info->type; + if (decl->var.init_expr) + { + if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return decl_poison(decl); + } + if (!sema_add_local(context, decl)) return decl_poison(decl); + return true; +} + +static inline Ast *convert_expr_to_ast(Expr *expr) +{ + Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span); + ast->expr_stmt = expr; + return ast; +} + + + +static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt) +{ + assert(stmt->ast_kind == AST_DECL_EXPR_LIST); + + VECEACH(stmt->decl_expr_stmt, i) + { + if (!sema_analyse_statement(context, stmt->decl_expr_stmt[i])) return false; + } + + return true; +} + +static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_bool) +{ + assert(stmt->ast_kind == AST_DECL_EXPR_LIST); + + size_t size = vec_size(stmt->decl_expr_stmt); + if (!size) + { + SEMA_ERROR(stmt, "Expected a boolean expression"); + return false; + } + + if (!sema_analyse_decl_expr_list(context, stmt)) return false; + + Ast *last = stmt->decl_expr_stmt[size - 1]; + switch (last->ast_kind) + { + case AST_EXPR_STMT: + if (cast_to_bool) + { + if (!cast_implicit(last->expr_stmt, type_bool)) return false; + } + return true; + case AST_DECLARE_STMT: + { + Expr *init = last->declare_stmt->var.init_expr; + if (!init) + { + SEMA_ERROR(last, "Expected a declaration with initializer."); + return false; + } + if (cast_to_bool && init->type->type_kind != TYPE_BOOL && + cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR) + { + SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean."); + return false; + } + return true; + } + default: + UNREACHABLE + } +} + +static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) +{ + Ast *decl = statement->while_stmt.decl; + Ast *cond = statement->while_stmt.cond; + Ast *body = statement->while_stmt.body; + context_push_scope(context); + bool success = !decl || sema_analyse_statement(context, decl); + context_push_scope(context); + + success = success && sema_analyse_cond(context, cond, true); + context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + success = success && sema_analyse_statement(context, body); + context_pop_defers_and_replace_ast(context, body); + context_pop_scope(context); + context_pop_defers_and_replace_ast(context, cond); + context_pop_scope(context); + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); + if (!success) return false; + return success; +} + +static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) +{ + Expr *expr = statement->do_stmt.expr; + Ast *body = statement->do_stmt.body; + bool success; + context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + success = sema_analyse_statement(context, body); + context_pop_defers_and_replace_ast(context, body); + context_pop_scope(context); + if (!success) return false; + context_push_scope(context); + success = sema_analyse_expr(context, type_bool, expr); + statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); + context_pop_scope(context); + return success; +} + + +static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) +{ + Decl *decl = statement->declare_stmt; + return sema_analyse_var_decl(context, decl); +} + +static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) +{ + if (!sema_analyse_expr(context, NULL, statement->expr_stmt)) return false; + return true; +} + +static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) +{ + // TODO special parsing of "catch" + context_push_scope_with_flags(context, SCOPE_DEFER | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + // Only ones allowed. + context->current_scope->flags &= SCOPE_DEFER | SCOPE_CONTINUE; // NOLINT(hicpp-signed-bitwise) + + bool success = sema_analyse_statement(context, statement->defer_stmt.body); + + context_pop_scope(context); + + if (!success) return false; + + statement->defer_stmt.prev_defer = context->current_scope->defers.start; + context->current_scope->defers.start = statement; + return true; +} + +static inline bool sema_analyse_default_stmt(Context *context __unused, Ast *statement) +{ + SEMA_ERROR(statement, "Unexpected 'default' outside of switch"); + return false; +} + +static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) +{ + bool success = true; + + // Enter for scope + context_push_scope(context); + if (statement->for_stmt.init) + { + success = sema_analyse_statement(context, statement->for_stmt.init); + } + if (success && statement->for_stmt.cond) + { + // Conditional scope start + context_push_scope(context); + Expr *cond = statement->for_stmt.cond; + success = sema_analyse_expr(context, type_bool, cond); + statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); + // Conditional scope end + context_pop_scope(context); + } + if (success && statement->for_stmt.incr) + { + // Incr scope start + context_push_scope(context); + Expr *incr = statement->for_stmt.incr; + success = sema_analyse_expr(context, NULL, incr); + statement->for_stmt.incr = context_pop_defers_and_wrap_expr(context, incr); + // Incr scope end + context_pop_scope(context); + } + if (!success) return false; + + // Create the for body scope. + context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) + success = sema_analyse_statement(context, statement->for_stmt.body); + // End for body scope + context_pop_defers_and_replace_ast(context, statement->for_stmt.body); + context_pop_scope(context); + context_pop_defers_and_replace_ast(context, statement); + // End for scope + context_pop_scope(context); + return success; +} + +static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement) +{ + + statement->goto_stmt.defer = context->current_scope->defers; + VECEACH(context->labels, i) + { + Ast *label = context->labels[i]; + if (statement->goto_stmt.label_name == label->label_stmt.name) + { + label->label_stmt.is_used = true; + statement->goto_stmt.label = label; + } + } + vec_add(context->gotos, statement); + context_add_exit(context, EXIT_GOTO); + return true; +} + +static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) +{ + // IMPROVE + // convert + // if (!x) A(); else B(); + // into + // if (x) B(); else A(); + Ast *cond = statement->if_stmt.cond; + context_push_scope(context); + bool success = sema_analyse_cond(context, cond, true); + if (statement->if_stmt.else_body) + { + if (statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) + { + SEMA_ERROR(statement->if_stmt.then_body, + "if-statements with an 'else' must use '{ }' even around a single statement."); + success = false; + } + if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT) + { + SEMA_ERROR(statement->if_stmt.else_body, + "An 'else' must use '{ }' even around a single statement."); + success = false; + } + } + success = success && sema_analyse_statement(context, statement->if_stmt.then_body); + if (statement->if_stmt.else_body) + { + success = success && sema_analyse_statement(context, statement->if_stmt.else_body); + } + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); + return success; +} + +static inline bool sema_analyse_label(Context *context, Ast *statement) +{ + statement->label_stmt.defer = context->current_scope->defers.start; + VECEACH(context->labels, i) + { + Ast *label = context->labels[i]; + if (label->label_stmt.name == statement->label_stmt.name) + { + SEMA_ERROR(statement, "This duplicate label '%s'.", statement->label_stmt.name); + sema_prev_at_range(label->span, "The previous declaration was here."); + ast_poison(label); + ast_poison(statement); + return false; + } + } + vec_add(context->labels, statement); + VECEACH(context->gotos, i) + { + Ast *the_goto = context->gotos[i]; + if (the_goto->goto_stmt.label_name == statement->label_stmt.name) + { + the_goto->goto_stmt.label = statement; + statement->label_stmt.is_used = true; + break; + } + } + return true; +} + + +static bool sema_analyse_catch_stmt(Context *context __unused, Ast *statement __unused) +{ + TODO +} + +static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __unused) +{ + TODO +} + + +static bool sema_analyse_break_stmt(Context *context, Ast *statement) +{ + if (!(context->current_scope->flags & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) + { + SEMA_ERROR(statement, "'break' is not allowed here."); + return false; + } + DynamicScope *scope = context->current_scope; + statement->break_stmt.defers.start = scope->defers.start; + while (!(scope->flags_created & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) + { + scope--; + } + statement->break_stmt.defers.end = scope->defers.end; + return true; +} + +static bool sema_analyse_next_stmt(Context *context, Ast *statement) +{ + if (!(context->current_scope->flags & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) + { + SEMA_ERROR(statement, "'next' is not allowed here."); + return false; + } + DynamicScope *scope = context->current_scope; + statement->next_stmt.defers.start = scope->defers.start; + while (!(scope->flags_created & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) + { + scope--; + } + statement->next_stmt.defers.end = scope->defers.end; + return true; +} + +static bool sema_analyse_case_stmt(Context *context, Ast *statement) +{ + SEMA_ERROR(statement, "Unexpected 'case' outside of switch"); + return false; +} + +static bool sema_analyse_continue_stmt(Context *context, Ast *statement) +{ + if (!(context->current_scope->flags & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) + { + SEMA_ERROR(statement, "'continue' is not allowed here."); + return false; + } + DynamicScope *scope = context->current_scope; + statement->continue_stmt.defers.start = scope->defers.start; + while (!(scope->flags_created & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) + { + scope--; + } + statement->continue_stmt.defers.end = scope->defers.end; + return true; +} + +static inline bool sema_analyse_then_overwrite(Context *context, Ast *statement, Ast *replacement) +{ + if (!sema_analyse_statement(context, replacement)) return false; + // Overwrite + *statement = *replacement; + return true; +} + + +static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement) +{ + int res = sema_check_comp_time_bool(context, statement->ct_if_stmt.expr); + if (res == -1) return false; + if (res) + { + return sema_analyse_then_overwrite(context, statement, statement->ct_if_stmt.then); + } + + Ast *elif = statement->ct_if_stmt.elif; + while (1) + { + if (!elif) + { + // Turn into NOP! + statement->ast_kind = AST_NOP_STMT; + return true; + } + // We found else, then just replace with that. + if (elif->ast_kind == AST_CT_ELSE_STMT) + { + return sema_analyse_then_overwrite(context, statement, elif->ct_else_stmt); + } + assert(elif->ast_kind == AST_CT_ELIF_STMT); + + res = sema_check_comp_time_bool(context, elif->ct_elif_stmt.expr); + if (res == -1) return false; + if (res) + { + return sema_analyse_then_overwrite(context, statement, elif->ct_elif_stmt.then); + } + elif = elif->ct_elif_stmt.elif; + } +} + + +static bool sema_analyse_case_expr(Context *context, Ast *case_stmt) +{ + Expr *case_expr = case_stmt->case_stmt.expr; + // TODO handle enums + if (!sema_analyse_expr(context, NULL, case_expr)) return false; + if (case_expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(case_expr, "This must be a constant expression."); + return false; + } + if (case_expr->const_expr.type != CONST_INT) + { + SEMA_ERROR(case_expr, "The 'case' value must be an integer constant."); + return false; + } + assert(case_expr->const_expr.type == CONST_INT); + case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT; + uint64_t val = case_expr->const_expr.i; + case_stmt->case_stmt.val = val; + return true; +} + + +static inline bool sema_analyse_compound_statement_no_scope(Context *context, Ast *compound_statement) +{ + bool all_ok = ast_ok(compound_statement); + VECEACH(compound_statement->compound_stmt.stmts, i) + { + if (!sema_analyse_statement(context, compound_statement->compound_stmt.stmts[i])) + { + ast_poison(compound_statement->compound_stmt.stmts[i]); + all_ok = false; + } + } + context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list); + + /* + if (parent->exit < compound_statement->exit) + { + parent->exit = compound_statement->exit; + }*/ + return all_ok; +} + +static inline Type *ast_cond_type(Ast *ast) +{ + assert(ast->ast_kind == AST_DECL_EXPR_LIST); + Ast *last = VECLAST(ast->decl_expr_stmt); + switch (last->ast_kind) + { + case AST_EXPR_STMT: + return last->expr_stmt->type; + case AST_DECLARE_STMT: + return last->declare_stmt->var.type_info->type; + default: + UNREACHABLE + } +} + +static bool sema_analyse_switch_stmt(Context *context, Ast *statement) +{ + context_push_scope(context); + Ast *cond = statement->switch_stmt.cond; + bool success = sema_analyse_cond(context, cond, false); + + Type *switch_type = ast_cond_type(cond)->canonical; + if (!type_is_integer(switch_type)) + { + SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); + return false; + } + Ast *default_case = NULL; + assert(context->current_scope->defers.start == context->current_scope->defers.end); + VECEACH(statement->switch_stmt.cases, i) + { + Ast *stmt = statement->switch_stmt.cases[i]; + switch (stmt->ast_kind) + { + case AST_CASE_STMT: + if (!sema_analyse_case_expr(context, stmt)) + { + success = false; + break; + } + for (unsigned j = 0; j < i; j++) + { + Ast *other = statement->switch_stmt.cases[j]; + if (other->ast_kind == AST_CASE_STMT && other->case_stmt.val == stmt->case_stmt.val) + { + SEMA_ERROR(stmt, "The same case value appears more than once."); + SEMA_PREV(other, "Here is the previous use of that value."); + success = false; + } + } + break; + case AST_DEFAULT_STMT: + if (default_case) + { + SEMA_ERROR(stmt, "'default' may only appear once in a single 'switch', please remove one."); + SEMA_PREV(default_case, "Here is the previous use."); + success = false; + } + default_case = stmt; + break; + default: + UNREACHABLE; + } + context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT); + success = success && sema_analyse_compound_statement_no_scope(context, stmt->case_stmt.body); + context_pop_scope(context); + } + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); + if (!success) return false; + // Is this a typeless switch value? + if (switch_type->type_kind == TYPE_UXX || switch_type->type_kind == TYPE_IXX) + { + + TODO + } + return success; +} + +static bool sema_analyse_try_stmt(Context *context, Ast *statement) +{ + context->try_nesting++; + unsigned errors = vec_size(context->errors); + if (!sema_analyse_statement(context, statement->try_stmt)) + { + context->try_nesting--; + return false; + } + unsigned new_errors = vec_size(context->errors); + if (new_errors == errors) + { + SEMA_ERROR(statement, "No error to 'try' in the statement that follows, please remove the 'try'."); + return false; + } + for (unsigned i = errors; i < new_errors; i++) + { + // At least one uncaught error found! + if (context->errors[i]) return true; + } + SEMA_ERROR(statement, "All errors in the following statement was caught, please remove the 'try'."); + return false; +} + +static bool sema_analyse_throw_stmt(Context *context, Ast *statement) +{ + Expr *throw_value = statement->throw_stmt.throw_value; + if (!sema_analyse_expr(context, NULL, throw_value)) return false; + Type *type = throw_value->type->canonical; + if (type->type_kind != TYPE_ERROR) + { + SEMA_ERROR(throw_value, "Only 'error' types can be thrown, this is a '%s'.", type->name); + return false; + } + if (!context->try_nesting && !func_has_error_return(&context->active_function_for_analysis->func.function_signature)) + { + SEMA_ERROR(statement, "This 'throw' is not handled, please add a 'throws %s' clause to the function signature or use try-catch.", type->name); + return false; + } + VECADD(context->errors, type->decl); + return true; +} + + +static bool sema_analyse_volatile_stmt(Context *context, Ast *statement) +{ + context->in_volatile_section++; + bool result = sema_analyse_statement(context, statement->volatile_stmt); + context->in_volatile_section--; + return result; +} + +static bool sema_analyse_compound_stmt(Context *context, Ast *statement) +{ + context_push_scope(context); + bool success = sema_analyse_compound_statement_no_scope(context, statement); + context_pop_scope(context); + return success; +} + + +static inline bool sema_analyse_statement_inner(Context *context, Ast *statement) +{ + switch (statement->ast_kind) + { + case AST_POISONED: + return false; + case AST_ATTRIBUTE: + case AST_SCOPED_STMT: + UNREACHABLE + case AST_ASM_STMT: + return sema_analyse_asm_stmt(context, statement); + case AST_BREAK_STMT: + return sema_analyse_break_stmt(context, statement); + case AST_CASE_STMT: + return sema_analyse_case_stmt(context, statement); + case AST_CATCH_STMT: + return sema_analyse_catch_stmt(context, statement); + case AST_COMPOUND_STMT: + return sema_analyse_compound_stmt(context, statement); + case AST_CONTINUE_STMT: + return sema_analyse_continue_stmt(context, statement); + case AST_CT_IF_STMT: + return sema_analyse_ct_if_stmt(context, statement); + case AST_DECLARE_STMT: + return sema_analyse_declare_stmt(context, statement); + case AST_DEFAULT_STMT: + return sema_analyse_default_stmt(context, statement); + case AST_DEFER_STMT: + return sema_analyse_defer_stmt(context, statement); + case AST_DO_STMT: + return sema_analyse_do_stmt(context, statement); + case AST_EXPR_STMT: + return sema_analyse_expr_stmt(context, statement); + case AST_FOR_STMT: + return sema_analyse_for_stmt(context, statement); + case AST_GOTO_STMT: + return sema_analyse_goto_stmt(context, statement); + case AST_IF_STMT: + return sema_analyse_if_stmt(context, statement); + case AST_LABEL: + return sema_analyse_label(context, statement); + case AST_NOP_STMT: + return true; + case AST_RETURN_STMT: + return sema_analyse_return_stmt(context, statement); + case AST_SWITCH_STMT: + return sema_analyse_switch_stmt(context, statement); + case AST_THROW_STMT: + return sema_analyse_throw_stmt(context, statement); + case AST_TRY_STMT: + return sema_analyse_try_stmt(context, statement); + case AST_NEXT_STMT: + return sema_analyse_next_stmt(context, statement); + case AST_VOLATILE_STMT: + return sema_analyse_volatile_stmt(context, statement); + case AST_WHILE_STMT: + return sema_analyse_while_stmt(context, statement); + case AST_DECL_EXPR_LIST: + return sema_analyse_decl_expr_list(context, statement); + case AST_CT_ELIF_STMT: + case AST_CT_ELSE_STMT: + UNREACHABLE + case AST_CT_FOR_STMT: + case AST_CT_SWITCH_STMT: + case AST_CT_DEFAULT_STMT: + case AST_CT_CASE_STMT: + case AST_GENERIC_CASE_STMT: + case AST_GENERIC_DEFAULT_STMT: + TODO + } +} + +bool sema_analyse_statement(Context *context, Ast *statement) +{ + if (sema_analyse_statement_inner(context, statement)) return true; + return ast_poison(statement); +} + +static inline int defer_depth(Ast *defer_stmt) +{ + int depth = 0; + while (defer_stmt) + { + defer_stmt = defer_stmt->defer_stmt.prev_defer; + depth++; + } + return depth; +} + +static inline void defer_list_walk_to_common_depth(Ast **defer_stmt, int this_depth, int other_depth) +{ + int steps = this_depth - other_depth; + for (int i = 0; i < steps; i++) + { + *defer_stmt = (*defer_stmt)->defer_stmt.prev_defer; + } +} + +bool sema_analyse_function_body(Context *context, Decl *func) +{ + context->active_function_for_analysis = func; + context->rtype = func->func.function_signature.rtype->type; + context->current_scope = &context->scopes[0]; + // Clean out the current scope. + memset(context->current_scope, 0, sizeof(*context->current_scope)); + + // Clear try handling + vec_resize(context->errors, 0); + // Clear returns + vec_resize(context->returns, 0); + context->try_nesting = 0; + context->labels = NULL; + context->gotos = NULL; + context->returns = NULL; + context->expected_block_type = NULL; + context->last_local = &context->locals[0]; + context->in_volatile_section = 0; + func->func.annotations = CALLOCS(*func->func.annotations); + context_push_scope(context); + Decl **params = func->func.function_signature.params; + VECEACH(params, i) + { + if (!sema_add_local(context, params[i])) return false; + } + if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; + if (context->current_scope->exit != EXIT_RETURN) + { + if (func->func.function_signature.rtype->type->canonical != type_void) + { + SEMA_ERROR(func, "Missing return statement at the end of the function."); + return false; + } + } + + VECEACH(context->gotos, i) + { + Ast *goto_stmt = context->gotos[i]; + Ast *label_target = goto_stmt->goto_stmt.label; + if (!label_target) + { + SEMA_ERROR(goto_stmt, "Goto to a missing label %s.", goto_stmt->goto_stmt.label_name); + return false; + } + + // If there are no defers, then that's fine. + if (!goto_stmt->goto_stmt.defer.start && !label_target->label_stmt.defer) continue; + + Ast *common_depth_label = label_target->label_stmt.defer; + Ast *common_depth_goto = goto_stmt->goto_stmt.defer.start; + + // First we need to search for the common depth. + int label_depth = defer_depth(common_depth_label); + int goto_depth = defer_depth(common_depth_goto); + + + // Now walk up to the common depth. + defer_list_walk_to_common_depth(&common_depth_label, label_depth, goto_depth); + defer_list_walk_to_common_depth(&common_depth_goto, goto_depth, label_depth); + + // We might still not match, so walk upwards until we have a match: + while (common_depth_goto != common_depth_label) + { + assert(common_depth_goto && common_depth_label); + common_depth_goto = common_depth_goto->defer_stmt.prev_defer; + common_depth_label = common_depth_label->defer_stmt.prev_defer; + } + + // We now know the top defer (which we won't actually generate) + goto_stmt->goto_stmt.defer.end = common_depth_goto; + + // Mark all defers that occur on the way "up" to the common depth conditional. + Ast *current = label_target->label_stmt.defer; + while (current != common_depth_goto) + { + current->defer_stmt.emit_boolean = true; + current = current->defer_stmt.prev_defer; + } + } + func->func.labels = context->labels; + context_pop_scope(context); + context->current_scope = NULL; + return true; +} + diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c new file mode 100644 index 000000000..b02ff3d17 --- /dev/null +++ b/src/compiler/sema_types.c @@ -0,0 +1,151 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "sema_internal.h" + + +static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) +{ + if (!sema_resolve_type_shallow(context, type_info->pointer)) + { + return type_info_poison(type_info); + } + type_info->type = type_get_ptr(type_info->pointer->type); + type_info->resolve_status = RESOLVE_DONE; + return true; +} + + +static inline bool sema_resolve_array_type(Context *context, TypeInfo *type) +{ + if (!sema_resolve_type_info(context, type->array.base)) + { + return type_info_poison(type); + } + if (type->array.len) + { + if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type); + if (type->array.len->expr_kind != EXPR_CONST) + { + SEMA_ERROR(type->array.len, "Expected a constant value as array size."); + return type_info_poison(type); + } + } + assert(!type->array.len || type->array.len->expr_kind == EXPR_CONST); + type->type = type_get_array(type->array.base->type, type->array.len ? type->array.len->const_expr.i : 0); + type->resolve_status = RESOLVE_DONE; + return true; +} + + +static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) +{ + Decl *ambiguous_decl; + Decl *decl = sema_resolve_symbol(context, + type_info->unresolved.name_loc.string, + type_info->unresolved.path, + &ambiguous_decl); + + if (!decl) + { + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string); + return type_info_poison(type_info); + } + + // Already handled + if (!decl_ok(decl)) + { + return type_info_poison(type_info); + } + + + if (ambiguous_decl) + { + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, + "Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", + type_info->unresolved.name_loc.string, + decl->module->name->module, + ambiguous_decl->module->name->module); + return type_info_poison(type_info); + } + switch (decl->decl_kind) + { + case DECL_THROWS: + TODO + case DECL_STRUCT: + case DECL_UNION: + case DECL_ERROR: + case DECL_ENUM: + type_info->type = decl->type; + type_info->resolve_status = RESOLVE_DONE; + DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string); + return true; + case DECL_TYPEDEF: + // TODO func + if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) + { + decl_poison(decl); + return type_info_poison(type_info); + } + DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string); + type_info->type = decl->type; + type_info->resolve_status = RESOLVE_DONE; + return true; + case DECL_POISONED: + return type_info_poison(type_info); + case DECL_FUNC: + case DECL_VAR: + case DECL_ENUM_CONSTANT: + case DECL_ERROR_CONSTANT: + case DECL_ARRAY_VALUE: + case DECL_IMPORT: + case DECL_MACRO: + case DECL_GENERIC: + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "This is not a type."); + return type_info_poison(type_info); + case DECL_CT_ELSE: + case DECL_CT_IF: + case DECL_CT_ELIF: + case DECL_ATTRIBUTE: + UNREACHABLE + } + UNREACHABLE + +} + +bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info) +{ + if (type_info->resolve_status == RESOLVE_DONE) return type_info_ok(type_info); + + if (type_info->resolve_status == RESOLVE_RUNNING) + { + // TODO this is incorrect for unresolved expressions + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, + "Circular dependency resolving type '%s'.", + type_info->unresolved.name_loc.string); + return type_info_poison(type_info); + } + + type_info->resolve_status = RESOLVE_RUNNING; + + switch (type_info->kind) + { + case TYPE_INFO_POISON: + case TYPE_INFO_INC_ARRAY: + UNREACHABLE + case TYPE_INFO_IDENTIFIER: + return sema_resolve_type_identifier(context, type_info); + case TYPE_INFO_EXPRESSION: + if (!sema_analyse_expr(context, NULL, type_info->unresolved_type_expr)) + { + return type_info_poison(type_info); + } + TODO + case TYPE_INFO_ARRAY: + return sema_resolve_array_type(context, type_info); + case TYPE_INFO_POINTER: + return sema_resolve_ptr_type(context, type_info); + } + +} \ No newline at end of file diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 795011897..cb77be662 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -2,23 +2,7 @@ // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. -#include "compiler_internal.h" - -static inline Type *ast_cond_type(Ast *ast) -{ - assert(ast->ast_kind == AST_DECL_EXPR_LIST); - Ast *last = VECLAST(ast->decl_expr_stmt); - switch (last->ast_kind) - { - case AST_EXPR_STMT: - return last->expr_stmt->type; - case AST_DECLARE_STMT: - return last->declare_stmt->var.type_info->type; - default: - UNREACHABLE - } -} - +#include "sema_internal.h" void sema_shadow_error(Decl *decl, Decl *old) { @@ -27,1581 +11,8 @@ void sema_shadow_error(Decl *decl, Decl *old) } -static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags) -{ - if (context->current_scope == &context->scopes[MAX_SCOPE_DEPTH - 1]) - { - FATAL_ERROR("Too deeply nested scopes."); - } - ScopeFlags previous_flags = context->current_scope->flags; - Ast *parent_defer = context->current_scope->defers.start; - context->current_scope++; - context->current_scope->exit = EXIT_NONE; - context->current_scope->local_decl_start = context->last_local; - context->current_scope->defers.start = parent_defer; - context->current_scope->defers.end = parent_defer; - context->current_scope->flags = previous_flags | flags; - context->current_scope->flags_created = flags; -} - -static inline void context_push_scope(Context *context) -{ - context_push_scope_with_flags(context, SCOPE_NONE); -} - -static inline void context_pop_defers(Context *context) -{ - context->current_scope->defers.start = context->current_scope->defers.end; -} - -static inline void context_pop_defers_to(Context *context, DeferList *list) -{ - *list = context->current_scope->defers; - context_pop_defers(context); -} - -static inline void context_add_exit(Context *context, ExitType exit) -{ - if (context->current_scope->exit < exit) - { - context->current_scope->exit = exit; - } -} - -static inline void context_pop_scope(Context *context) -{ - assert(context->current_scope != &context->scopes[0]); - context->last_local = context->current_scope->local_decl_start; - ExitType exit_type = context->current_scope->exit; - assert (context->current_scope->defers.end == context->current_scope->defers.start); - context->current_scope--; - if (context->current_scope->exit < exit_type) - { - context->current_scope->exit = exit_type; - } -} - -static Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr) -{ - DeferList defers = { NULL, NULL }; - context_pop_defers_to(context, &defers); - if (defers.end == defers.start) return expr; - Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span); - wrap->type = expr->type; - wrap->resolve_status = RESOLVE_DONE; - wrap->expr_scope.expr = expr; - wrap->expr_scope.defers = defers; - return expr; -} - -static void context_pop_defers_and_replace_ast(Context *context, Ast *ast) -{ - DeferList defers = { NULL, NULL }; - context_pop_defers_to(context, &defers); - if (defers.end == defers.start) return; - if (ast->ast_kind == AST_DEFER_STMT) - { - assert(defers.start == ast); - *ast = *ast->defer_stmt.body; - return; - } - assert(ast->ast_kind != AST_COMPOUND_STMT); - Ast *replacement = malloc_arena(sizeof(Ast)); - *replacement = *ast; - ast->ast_kind = AST_SCOPED_STMT; - ast->scoped_stmt.stmt = replacement; - ast->scoped_stmt.defers = defers; -} - - -static bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) -{ - if (!sema_resolve_type_shallow(context, type_info->pointer)) - { - return type_info_poison(type_info); - } - type_info->type = type_get_ptr(type_info->pointer->type); - type_info->resolve_status = RESOLVE_DONE; - return true; -} - - -static bool sema_resolve_array_type(Context *context, TypeInfo *type) -{ - if (!sema_resolve_type_info(context, type->array.base)) - { - return type_info_poison(type); - } - if (type->array.len) - { - if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type); - if (type->array.len->expr_kind != EXPR_CONST) - { - SEMA_ERROR(type->array.len, "Expected a constant value as array size."); - return type_info_poison(type); - } - } - assert(!type->array.len || type->array.len->expr_kind == EXPR_CONST); - type->type = type_get_array(type->array.base->type, type->array.len ? type->array.len->const_expr.i : 0); - type->resolve_status = RESOLVE_DONE; - return true; -} - -static inline bool sema_analyse_struct_member(Context *context, Decl *decl) -{ - if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION) - { - DEBUG_LOG("Beginning analysis of inner struct/union"); - VECEACH(decl->strukt.members, i) - { - Decl *member = decl->strukt.members[i]; - if (!decl_ok(member)) - { - decl_poison(decl); - continue; - } - if (!sema_analyse_struct_member(context, decl->strukt.members[i])) - { - if (decl_ok(decl)) - { - decl_poison(decl); - continue; - } - decl_poison(decl); - } - } - DEBUG_LOG("Analysis complete."); - return decl_ok(decl); - } - assert(decl->decl_kind == DECL_VAR); - assert(decl->var.kind == VARDECL_MEMBER); - assert(!decl->var.init_expr); - if (!sema_resolve_type_info(context, decl->var.type_info)) - { - decl_poison(decl); - return false; - } - decl->type = decl->var.type_info->type; - assert(decl->var.type_info->type); - return true; -} - -static inline bool sema_analyse_struct_union(Context *context, Decl *decl) -{ - DEBUG_LOG("Beginning analysis of %s.", decl->name); - assert(decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION); - VECEACH(decl->strukt.members, i) - { - Decl *member = decl->strukt.members[i]; - if (!decl_ok(member)) - { - decl_poison(decl); - continue; - } - if (!sema_analyse_struct_member(context, decl->strukt.members[i])) - { - if (decl_ok(decl)) - { - decl_poison(decl); - continue; - } - decl_poison(decl); - } - } - DEBUG_LOG("Analysis complete."); - return decl_ok(decl); -} - - -static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function) -{ - assert(param->decl_kind == DECL_VAR); - assert(param->var.kind == VARDECL_PARAM); - if (!sema_resolve_type_info(context, param->var.type_info)) - { - return false; - } - param->type = param->var.type_info->type; - if (param->var.init_expr && !is_function) - { - SEMA_ERROR(param->var.init_expr, "Function types may not have default arguments."); - return false; - } - if (param->var.init_expr) - { - Expr *expr = param->var.init_expr; - if (!sema_analyse_expr(context, param->type, expr)) return false; - if (expr->expr_kind != EXPR_CONST) - { - SEMA_ERROR(expr, "Only constant expressions may be used as default values."); - return false; - } - } - return true; -} - -static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function) -{ - char buffer[MAX_FUNCTION_SIGNATURE_SIZE + 200]; - size_t buffer_write_offset = 0; - bool all_ok = true; - all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok; - if (all_ok) - { - type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset); - buffer[buffer_write_offset++] = '('; - } - if (vec_size(signature->params) > MAX_PARAMS) - { - SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS); - return false; - } - STable *names = &context->scratch_table; - stable_clear(names); - - VECEACH(signature->params, i) - { - Decl *param = signature->params[i]; - assert(param->resolve_status == RESOLVE_NOT_DONE); - param->resolve_status = RESOLVE_RUNNING; - if (!sema_analyse_function_param(context, param, is_function)) - { - decl_poison(param); - all_ok = false; - continue; - } - param->resolve_status = RESOLVE_DONE; - if (i > 0 && all_ok) - { - buffer[buffer_write_offset++] = ','; - } - type_append_signature_name(param->var.type_info->type, buffer, &buffer_write_offset); - if (param->name) - { - Decl *prev = stable_set(names, param->name, param); - if (prev) - { - SEMA_ERROR(param, "Duplicate parameter name %s.", param->name); - SEMA_PREV(prev, "Previous use of the name was here."); - decl_poison(prev); - decl_poison(param); - all_ok = false; - } - } - } - if (signature->variadic) - { - buffer[buffer_write_offset++] = ','; - buffer[buffer_write_offset++] = '.'; - buffer[buffer_write_offset++] = '.'; - buffer[buffer_write_offset++] = '.'; - } - buffer[buffer_write_offset++] = ')'; - if (signature->throw_any) - { - assert(!signature->throws); - buffer[buffer_write_offset++] = '!'; - } - if (vec_size(signature->throws)) - { - buffer[buffer_write_offset++] = '!'; - VECEACH(signature->throws, i) - { - Decl *err_decl = signature->throws[i]; - if (!sema_analyse_decl(context, err_decl)) - { - continue; - } - if (i > 0 && all_ok) - { - buffer[buffer_write_offset++] = '|'; - } - type_append_signature_name(err_decl->type, buffer, &buffer_write_offset); - } - } - if (!all_ok) return NULL; - TokenType type = TOKEN_INVALID_TOKEN; - signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type); - Type *func_type = stable_get(&context->local_symbols, signature->mangled_signature); - if (!func_type) - { - func_type = type_new(TYPE_FUNC, signature->mangled_signature); - func_type->canonical = func_type; - func_type->func.signature = signature; - stable_set(&context->local_symbols, signature->mangled_signature, func_type); - } - return func_type; - -} - - -static inline bool sema_analyse_compound_statement_no_scope(Context *context, Ast *compound_statement) -{ - bool all_ok = ast_ok(compound_statement); - VECEACH(compound_statement->compound_stmt.stmts, i) - { - if (!sema_analyse_statement(context, compound_statement->compound_stmt.stmts[i])) - { - ast_poison(compound_statement->compound_stmt.stmts[i]); - all_ok = false; - } - } - context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list); - - /* - if (parent->exit < compound_statement->exit) - { - parent->exit = compound_statement->exit; - }*/ - return all_ok; -} - -static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) -{ - context->current_scope->exit = EXIT_RETURN; - Type *expected_rtype = context->rtype; - Expr *return_expr = statement->return_stmt.expr; - statement->return_stmt.defer = context->current_scope->defers.start; - if (return_expr == NULL) - { - if (!expected_rtype) - { - assert(context->evaluating_macro); - context->rtype = type_void; - return true; - } - if (expected_rtype->canonical != type_void) - { - SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype)); - return false; - } - return true; - } - if (!sema_analyse_expr(context, expected_rtype, return_expr)) return false; - if (!expected_rtype) - { - assert(context->evaluating_macro); - context->rtype = type_void; - context->active_function_for_analysis->func.function_signature.rtype->type->canonical = statement->return_stmt.expr->type->canonical; - return true; - } - if (context->evaluating_macro && expected_rtype->canonical != return_expr->type->canonical) - { - TODO // Fix upcast - } - if (!cast(statement->return_stmt.expr, expected_rtype, CAST_TYPE_IMPLICIT)) return false; - return true; -} - - -static inline bool sema_analyse_var_decl(Context *context, Decl *decl) -{ - assert(decl->decl_kind == DECL_VAR); - if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl); - decl->type = decl->var.type_info->type; - if (decl->var.init_expr) - { - if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return decl_poison(decl); - } - if (!sema_add_local(context, decl)) return decl_poison(decl); - return true; -} - -static inline Ast *convert_expr_to_ast(Expr *expr) -{ - Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span); - ast->expr_stmt = expr; - return ast; -} - - -static inline bool sema_analyse_function_block_stmt(Context *context, Ast *stmt) -{ - TODO -} - -static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt) -{ - assert(stmt->ast_kind == AST_DECL_EXPR_LIST); - - VECEACH(stmt->decl_expr_stmt, i) - { - if (!sema_analyse_statement(context, stmt->decl_expr_stmt[i])) return false; - } - - return true; -} - -static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_bool) -{ - assert(stmt->ast_kind == AST_DECL_EXPR_LIST); - - size_t size = vec_size(stmt->decl_expr_stmt); - if (!size) - { - SEMA_ERROR(stmt, "Expected a boolean expression"); - return false; - } - - if (!sema_analyse_decl_expr_list(context, stmt)) return false; - - Ast *last = stmt->decl_expr_stmt[size - 1]; - switch (last->ast_kind) - { - case AST_EXPR_STMT: - if (cast_to_bool) - { - if (!cast_implicit(last->expr_stmt, type_bool)) return false; - } - return true; - case AST_DECLARE_STMT: - { - Expr *init = last->declare_stmt->var.init_expr; - if (!init) - { - SEMA_ERROR(last, "Expected a declaration with initializer."); - return false; - } - if (cast_to_bool && init->type->type_kind != TYPE_BOOL && - cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR) - { - SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean."); - return false; - } - return true; - } - default: - UNREACHABLE - } -} - -static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) -{ - Ast *decl = statement->while_stmt.decl; - Ast *cond = statement->while_stmt.cond; - Ast *body = statement->while_stmt.body; - context_push_scope(context); - bool success = !decl || sema_analyse_statement(context, decl); - context_push_scope(context); - - success = success && sema_analyse_cond(context, cond, true); - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) - success = success && sema_analyse_statement(context, body); - context_pop_defers_and_replace_ast(context, body); - context_pop_scope(context); - context_pop_defers_and_replace_ast(context, cond); - context_pop_scope(context); - context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); - if (!success) return false; - return success; -} - -static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) -{ - Expr *expr = statement->do_stmt.expr; - Ast *body = statement->do_stmt.body; - bool success; - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) - success = sema_analyse_statement(context, body); - context_pop_defers_and_replace_ast(context, body); - context_pop_scope(context); - if (!success) return false; - context_push_scope(context); - success = sema_analyse_expr(context, type_bool, expr); - statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); - context_pop_scope(context); - return success; -} - - -static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) -{ - Decl *decl = statement->declare_stmt; - return sema_analyse_var_decl(context, decl); -} - -static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) -{ - if (!sema_analyse_expr(context, NULL, statement->expr_stmt)) return false; - return true; -} - -static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) -{ - context_push_scope_with_flags(context, SCOPE_DEFER | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) - // Only ones allowed. - context->current_scope->flags &= SCOPE_DEFER | SCOPE_CONTINUE; // NOLINT(hicpp-signed-bitwise) - - bool success = sema_analyse_statement(context, statement->defer_stmt.body); - - context_pop_scope(context); - - if (!success) return false; - - statement->defer_stmt.prev_defer = context->current_scope->defers.start; - context->current_scope->defers.start = statement; - return true; -} - -static inline bool sema_analyse_default_stmt(Context *context __unused, Ast *statement) -{ - SEMA_ERROR(statement, "Unexpected 'default' outside of switch"); - return false; -} - -static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) -{ - bool success = true; - - // Enter for scope - context_push_scope(context); - if (statement->for_stmt.init) - { - success = sema_analyse_statement(context, statement->for_stmt.init); - } - if (success && statement->for_stmt.cond) - { - // Conditional scope start - context_push_scope(context); - Expr *cond = statement->for_stmt.cond; - success = sema_analyse_expr(context, type_bool, cond); - statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); - // Conditional scope end - context_pop_scope(context); - } - if (success && statement->for_stmt.incr) - { - // Incr scope start - context_push_scope(context); - Expr *incr = statement->for_stmt.incr; - success = sema_analyse_expr(context, NULL, incr); - statement->for_stmt.incr = context_pop_defers_and_wrap_expr(context, incr); - // Incr scope end - context_pop_scope(context); - } - if (!success) return false; - - // Create the for body scope. - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) - success = sema_analyse_statement(context, statement->for_stmt.body); - // End for body scope - context_pop_defers_and_replace_ast(context, statement->for_stmt.body); - context_pop_scope(context); - context_pop_defers_and_replace_ast(context, statement); - // End for scope - context_pop_scope(context); - return success; -} - -static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement) -{ - - statement->goto_stmt.defer = context->current_scope->defers; - VECEACH(context->labels, i) - { - Ast *label = context->labels[i]; - if (statement->goto_stmt.label_name == label->label_stmt.name) - { - label->label_stmt.is_used = true; - statement->goto_stmt.label = label; - } - } - vec_add(context->gotos, statement); - context_add_exit(context, EXIT_GOTO); - return true; -} - -static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) -{ - // IMPROVE - // convert - // if (!x) A(); else B(); - // into - // if (x) B(); else A(); - Ast *cond = statement->if_stmt.cond; - context_push_scope(context); - bool success = sema_analyse_cond(context, cond, true); - if (statement->if_stmt.else_body) - { - if (statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) - { - SEMA_ERROR(statement->if_stmt.then_body, - "if-statements with an 'else' must use '{ }' even around a single statement."); - success = false; - } - if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT) - { - SEMA_ERROR(statement->if_stmt.else_body, - "An 'else' must use '{ }' even around a single statement."); - success = false; - } - } - success = success && sema_analyse_statement(context, statement->if_stmt.then_body); - if (statement->if_stmt.else_body) - { - success = success && sema_analyse_statement(context, statement->if_stmt.else_body); - } - context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); - return success; -} - -static inline bool sema_analyse_label(Context *context, Ast *statement) -{ - statement->label_stmt.defer = context->current_scope->defers.start; - VECEACH(context->labels, i) - { - Ast *label = context->labels[i]; - if (label->label_stmt.name == statement->label_stmt.name) - { - SEMA_ERROR(statement, "This duplicate label '%s'.", statement->label_stmt.name); - sema_prev_at_range(label->span, "The previous declaration was here."); - ast_poison(label); - ast_poison(statement); - return false; - } - } - vec_add(context->labels, statement); - VECEACH(context->gotos, i) - { - Ast *the_goto = context->gotos[i]; - if (the_goto->goto_stmt.label_name == statement->label_stmt.name) - { - the_goto->goto_stmt.label = statement; - statement->label_stmt.is_used = true; - break; - } - } - return true; -} - - -static bool sema_analyse_catch_stmt(Context *context __unused, Ast *statement __unused) -{ - TODO -} - -static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __unused) -{ - TODO -} - - -static bool sema_analyse_break_stmt(Context *context, Ast *statement) -{ - if (!(context->current_scope->flags & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) - { - SEMA_ERROR(statement, "'break' is not allowed here."); - return false; - } - DynamicScope *scope = context->current_scope; - statement->break_stmt.defers.start = scope->defers.start; - while (!(scope->flags_created & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) - { - scope--; - } - statement->break_stmt.defers.end = scope->defers.end; - return true; -} - -static bool sema_analyse_next_stmt(Context *context, Ast *statement) -{ - if (!(context->current_scope->flags & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) - { - SEMA_ERROR(statement, "'next' is not allowed here."); - return false; - } - DynamicScope *scope = context->current_scope; - statement->next_stmt.defers.start = scope->defers.start; - while (!(scope->flags_created & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) - { - scope--; - } - statement->next_stmt.defers.end = scope->defers.end; - return true; -} - -static bool sema_analyse_case_stmt(Context *context, Ast *statement) -{ - SEMA_ERROR(statement, "Unexpected 'case' outside of switch"); - return false; -} - -static bool sema_analyse_continue_stmt(Context *context, Ast *statement) -{ - if (!(context->current_scope->flags & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) - { - SEMA_ERROR(statement, "'continue' is not allowed here."); - return false; - } - DynamicScope *scope = context->current_scope; - statement->continue_stmt.defers.start = scope->defers.start; - while (!(scope->flags_created & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) - { - scope--; - } - statement->continue_stmt.defers.end = scope->defers.end; - return true; -} - -static inline bool sema_analyse_then_overwrite(Context *context, Ast *statement, Ast *replacement) -{ - if (!sema_analyse_statement(context, replacement)) return false; - // Overwrite - *statement = *replacement; - return true; -} - -static int sema_check_comp_time_bool(Context *context, Expr *expr) -{ - if (!sema_analyse_expr(context, type_bool, expr)) return -1; - if (expr->expr_kind != EXPR_CONST) - { - SEMA_ERROR(expr, "$if requires a compile time constant value."); - return -1; - } - return expr->const_expr.b; -} - -static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement) -{ - int res = sema_check_comp_time_bool(context, statement->ct_if_stmt.expr); - if (res == -1) return false; - if (res) - { - return sema_analyse_then_overwrite(context, statement, statement->ct_if_stmt.then); - } - - Ast *elif = statement->ct_if_stmt.elif; - while (1) - { - if (!elif) - { - // Turn into NOP! - statement->ast_kind = AST_NOP_STMT; - return true; - } - // We found else, then just replace with that. - if (elif->ast_kind == AST_CT_ELSE_STMT) - { - return sema_analyse_then_overwrite(context, statement, elif->ct_else_stmt); - } - assert(elif->ast_kind == AST_CT_ELIF_STMT); - - res = sema_check_comp_time_bool(context, elif->ct_elif_stmt.expr); - if (res == -1) return false; - if (res) - { - return sema_analyse_then_overwrite(context, statement, elif->ct_elif_stmt.then); - } - elif = elif->ct_elif_stmt.elif; - } -} - - -static bool sema_analyse_case_expr(Context *context, Ast *case_stmt) -{ - Expr *case_expr = case_stmt->case_stmt.expr; - // TODO handle enums - if (!sema_analyse_expr(context, NULL, case_expr)) return false; - if (case_expr->expr_kind != EXPR_CONST) - { - SEMA_ERROR(case_expr, "This must be a constant expression."); - return false; - } - if (case_expr->const_expr.type != CONST_INT) - { - SEMA_ERROR(case_expr, "The 'case' value must be an integer constant."); - return false; - } - assert(case_expr->const_expr.type == CONST_INT); - case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT; - uint64_t val = case_expr->const_expr.i; - case_stmt->case_stmt.val = val; - return true; -} - -static bool sema_analyse_switch_stmt(Context *context, Ast *statement) -{ - context_push_scope(context); - Ast *cond = statement->switch_stmt.cond; - bool success = sema_analyse_cond(context, cond, false); - - Type *switch_type = ast_cond_type(cond)->canonical; - if (!type_is_integer(switch_type)) - { - SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); - return false; - } - Ast *default_case = NULL; - assert(context->current_scope->defers.start == context->current_scope->defers.end); - VECEACH(statement->switch_stmt.cases, i) - { - Ast *stmt = statement->switch_stmt.cases[i]; - switch (stmt->ast_kind) - { - case AST_CASE_STMT: - if (!sema_analyse_case_expr(context, stmt)) - { - success = false; - break; - } - for (unsigned j = 0; j < i; j++) - { - Ast *other = statement->switch_stmt.cases[j]; - if (other->ast_kind == AST_CASE_STMT && other->case_stmt.val == stmt->case_stmt.val) - { - SEMA_ERROR(stmt, "The same case value appears more than once."); - SEMA_PREV(other, "Here is the previous use of that value."); - success = false; - } - } - break; - case AST_DEFAULT_STMT: - if (default_case) - { - SEMA_ERROR(stmt, "'default' may only appear once in a single 'switch', please remove one."); - SEMA_PREV(default_case, "Here is the previous use."); - success = false; - } - default_case = stmt; - break; - default: - UNREACHABLE; - } - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT); - success = success && sema_analyse_compound_statement_no_scope(context, stmt->case_stmt.body); - context_pop_scope(context); - } - context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); - if (!success) return false; - // Is this a typeless switch value? - if (switch_type->type_kind == TYPE_UXX || switch_type->type_kind == TYPE_IXX) - { - - TODO - } - return success; -} - -static bool sema_analyse_try_stmt(Context *context, Ast *statement) -{ - context->try_nesting++; - unsigned errors = vec_size(context->errors); - if (!sema_analyse_statement(context, statement->try_stmt)) - { - context->try_nesting--; - return false; - } - unsigned new_errors = vec_size(context->errors); - if (new_errors == errors) - { - SEMA_ERROR(statement, "No error to 'try' in the statement that follows, please remove the 'try'."); - return false; - } - for (unsigned i = errors; i < new_errors; i++) - { - // At least one uncaught error found! - if (context->errors[i]) return true; - } - SEMA_ERROR(statement, "All errors in the following statement was caught, please remove the 'try'."); - return false; -} - -static bool sema_analyse_throw_stmt(Context *context, Ast *statement) -{ - Expr *throw_value = statement->throw_stmt.throw_value; - if (!sema_analyse_expr(context, NULL, throw_value)) return false; - Type *type = throw_value->type->canonical; - if (type->type_kind != TYPE_ERROR) - { - SEMA_ERROR(throw_value, "Only 'error' types can be thrown, this is a '%s'.", type->name); - return false; - } - if (!context->try_nesting && !func_has_error_return(&context->active_function_for_analysis->func.function_signature)) - { - SEMA_ERROR(statement, "This 'throw' is not handled, please add a 'throws %s' clause to the function signature or use try-catch.", type->name); - return false; - } - VECADD(context->errors, type->decl); - return true; -} - - -static bool sema_analyse_volatile_stmt(Context *context, Ast *statement) -{ - context->in_volatile_section++; - bool result = sema_analyse_statement(context, statement->volatile_stmt); - context->in_volatile_section--; - return result; -} - -static bool sema_analyse_compound_stmt(Context *context, Ast *statement) -{ - context_push_scope(context); - bool success = sema_analyse_compound_statement_no_scope(context, statement); - context_pop_scope(context); - return success; -} - -static inline bool sema_analyse_statement_inner(Context *context, Ast *statement) -{ - switch (statement->ast_kind) - { - case AST_POISONED: - return false; - case AST_ATTRIBUTE: - case AST_SCOPED_STMT: - UNREACHABLE - case AST_ASM_STMT: - return sema_analyse_asm_stmt(context, statement); - case AST_BREAK_STMT: - return sema_analyse_break_stmt(context, statement); - case AST_CASE_STMT: - return sema_analyse_case_stmt(context, statement); - case AST_CATCH_STMT: - return sema_analyse_catch_stmt(context, statement); - case AST_COMPOUND_STMT: - return sema_analyse_compound_stmt(context, statement); - case AST_CONTINUE_STMT: - return sema_analyse_continue_stmt(context, statement); - case AST_CT_IF_STMT: - return sema_analyse_ct_if_stmt(context, statement); - case AST_DECLARE_STMT: - return sema_analyse_declare_stmt(context, statement); - case AST_DEFAULT_STMT: - return sema_analyse_default_stmt(context, statement); - case AST_DEFER_STMT: - return sema_analyse_defer_stmt(context, statement); - case AST_DO_STMT: - return sema_analyse_do_stmt(context, statement); - case AST_EXPR_STMT: - return sema_analyse_expr_stmt(context, statement); - case AST_FOR_STMT: - return sema_analyse_for_stmt(context, statement); - case AST_GOTO_STMT: - return sema_analyse_goto_stmt(context, statement); - case AST_IF_STMT: - return sema_analyse_if_stmt(context, statement); - case AST_LABEL: - return sema_analyse_label(context, statement); - case AST_NOP_STMT: - return true; - case AST_RETURN_STMT: - return sema_analyse_return_stmt(context, statement); - case AST_SWITCH_STMT: - return sema_analyse_switch_stmt(context, statement); - case AST_THROW_STMT: - return sema_analyse_throw_stmt(context, statement); - case AST_TRY_STMT: - return sema_analyse_try_stmt(context, statement); - case AST_NEXT_STMT: - return sema_analyse_next_stmt(context, statement); - case AST_VOLATILE_STMT: - return sema_analyse_volatile_stmt(context, statement); - case AST_WHILE_STMT: - return sema_analyse_while_stmt(context, statement); - case AST_DECL_EXPR_LIST: - return sema_analyse_decl_expr_list(context, statement); - case AST_FUNCTION_BLOCK_STMT: - return sema_analyse_function_block_stmt(context, statement); - case AST_CT_ELIF_STMT: - case AST_CT_ELSE_STMT: - UNREACHABLE - case AST_CT_FOR_STMT: - case AST_CT_SWITCH_STMT: - case AST_CT_DEFAULT_STMT: - case AST_CT_CASE_STMT: - case AST_GENERIC_CASE_STMT: - case AST_GENERIC_DEFAULT_STMT: - TODO - } -} - -bool sema_analyse_statement(Context *context, Ast *statement) -{ - if (sema_analyse_statement_inner(context, statement)) return true; - return ast_poison(statement); -} - -static inline int defer_depth(Ast *defer_stmt) -{ - int depth = 0; - while (defer_stmt) - { - defer_stmt = defer_stmt->defer_stmt.prev_defer; - depth++; - } - return depth; -} - -static inline void defer_list_walk_to_common_depth(Ast **defer_stmt, int this_depth, int other_depth) -{ - int steps = this_depth - other_depth; - for (int i = 0; i < steps; i++) - { - *defer_stmt = (*defer_stmt)->defer_stmt.prev_defer; - } -} - -static inline bool sema_analyse_function_body(Context *context, Decl *func) -{ - context->active_function_for_analysis = func; - context->rtype = func->func.function_signature.rtype->type; - context->current_scope = &context->scopes[0]; - // Clean out the current scope. - memset(context->current_scope, 0, sizeof(*context->current_scope)); - - // Clear try handling - vec_resize(context->errors, 0); - context->try_nesting = 0; - - - context->labels = NULL; - context->gotos = NULL; - context->last_local = &context->locals[0]; - context->in_volatile_section = 0; - func->func.annotations = CALLOCS(*func->func.annotations); - context_push_scope(context); - Decl **params = func->func.function_signature.params; - VECEACH(params, i) - { - if (!sema_add_local(context, params[i])) return false; - } - if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; - if (context->current_scope->exit != EXIT_RETURN) - { - if (func->func.function_signature.rtype->type->canonical != type_void) - { - SEMA_ERROR(func, "Missing return statement at the end of the function."); - return false; - } - } - - VECEACH(context->gotos, i) - { - Ast *goto_stmt = context->gotos[i]; - Ast *label_target = goto_stmt->goto_stmt.label; - if (!label_target) - { - SEMA_ERROR(goto_stmt, "Goto to a missing label %s.", goto_stmt->goto_stmt.label_name); - return false; - } - - // If there are no defers, then that's fine. - if (!goto_stmt->goto_stmt.defer.start && !label_target->label_stmt.defer) continue; - - Ast *common_depth_label = label_target->label_stmt.defer; - Ast *common_depth_goto = goto_stmt->goto_stmt.defer.start; - - // First we need to search for the common depth. - int label_depth = defer_depth(common_depth_label); - int goto_depth = defer_depth(common_depth_goto); - - - // Now walk up to the common depth. - defer_list_walk_to_common_depth(&common_depth_label, label_depth, goto_depth); - defer_list_walk_to_common_depth(&common_depth_goto, goto_depth, label_depth); - - // We might still not match, so walk upwards until we have a match: - while (common_depth_goto != common_depth_label) - { - assert(common_depth_goto && common_depth_label); - common_depth_goto = common_depth_goto->defer_stmt.prev_defer; - common_depth_label = common_depth_label->defer_stmt.prev_defer; - } - - // We now know the top defer (which we won't actually generate) - goto_stmt->goto_stmt.defer.end = common_depth_goto; - - // Mark all defers that occur on the way "up" to the common depth conditional. - Ast *current = label_target->label_stmt.defer; - while (current != common_depth_goto) - { - current->defer_stmt.emit_boolean = true; - current = current->defer_stmt.prev_defer; - } - } - func->func.labels = context->labels; - context_pop_scope(context); - context->current_scope = NULL; - return true; -} - - -static inline bool sema_analyse_method_function(Context *context, Decl *decl) -{ - TypeInfo *parent_type = decl->func.type_parent; - if (!sema_resolve_type_info(context, parent_type)) return false; - if (!type_may_have_method_functions(parent_type->type)) - { - SEMA_ERROR(decl, - "Method functions can not be associated with '%s'", - type_to_error_string(decl->func.type_parent->type)); - return false; - } - Decl *parent = parent_type->type->decl; - VECEACH(parent->method_functions, i) - { - Decl *function = parent->method_functions[i]; - if (function->name == decl->name) - { - SEMA_ERROR(decl, "Duplicate name '%s' for method function.", function->name); - SEMA_PREV(function, "Previous definition here."); - return false; - } - } - DEBUG_LOG("Method function '%s.%s' analysed.", parent->name, decl->name); - vec_add(parent->method_functions, decl); - return true; -} - -static inline bool sema_analyse_func(Context *context, Decl *decl) -{ - DEBUG_LOG("Analysing function %s", decl->name); - Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true); - decl->type = func_type; - if (!func_type) return decl_poison(decl); - if (decl->func.type_parent) - { - if (!sema_analyse_method_function(context, decl)) return decl_poison(decl); - } - if (decl->func.body && !sema_analyse_function_body(context, decl)) return decl_poison(decl); - if (decl->name == main_name) - { - if (decl->visibility == VISIBLE_LOCAL) - { - SEMA_ERROR(decl, "'main' cannot have local visibility."); - return false; - } - decl->visibility = VISIBLE_EXTERN; - } - DEBUG_LOG("Function analysis done."); - return true; -} - -static inline bool sema_analyse_macro(Context *context, Decl *decl) -{ - TypeInfo *rtype = decl->macro_decl.rtype; - if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return false; - VECEACH(decl->macro_decl.parameters, i) - { - Decl *param = decl->macro_decl.parameters[i]; - assert(param->decl_kind == DECL_VAR); - assert(param->var.kind == VARDECL_PARAM); - if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false; - } - return true; -} - -static inline bool sema_analyse_global(Context *context, Decl *decl) -{ - if (!sema_resolve_type_info(context, decl->var.type_info)) return false; - if (decl->var.init_expr) - { - if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return false; - if (decl->var.init_expr->expr_kind != EXPR_CONST) - { - SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); - return false; - } - } - switch (decl->var.kind) - { - case VARDECL_CONST: - assert(decl->var.init_expr); - return true; - case VARDECL_GLOBAL: - return true; - default: - UNREACHABLE - break; - } -} - -static inline bool sema_analyse_typedef(Context *context, Decl *decl) -{ - if (decl->typedef_decl.is_func) - { - Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false); - if (!func_type) return false; - decl->type->canonical = func_type; - return true; - } - if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false; - decl->type->canonical = decl->typedef_decl.type_info->type; - // Do we need anything else? - return true; -} - -static inline bool sema_analyse_generic(Context *context, Decl *decl) -{ - TODO - return true; -} - -static inline bool sema_analyse_enum(Context *context, Decl *decl) -{ - if (!sema_resolve_type_info(context, decl->enums.type_info)) return false; - uint64_t value = 0; - Type *type = decl->enums.type_info->type; - bool success = true; - VECEACH(decl->enums.values, i) - { - Decl *enum_value = decl->enums.values[i]; - enum_value->enum_constant.ordinal = i; - assert(enum_value->resolve_status == RESOLVE_NOT_DONE); - assert(enum_value->decl_kind == DECL_ENUM_CONSTANT); - enum_value->resolve_status = RESOLVE_RUNNING; - Expr *expr = enum_value->enum_constant.expr; - if (!expr) - { - expr = expr_new(EXPR_CONST, INVALID_RANGE); - expr->type = type; - expr->resolve_status = RESOLVE_DONE; - expr->const_expr.type = CONST_INT; - expr->const_expr.i = value; - enum_value->enum_constant.expr = expr; - } - if (!sema_analyse_expr(context, type, expr)) - { - success = false; - enum_value->resolve_status = RESOLVE_DONE; - continue; - } - assert(type_is_integer(expr->type->canonical)); - if (expr->expr_kind != EXPR_CONST) - { - SEMA_ERROR(expr, "Expected a constant expression for enum"); - success = false; - } - enum_value->resolve_status = RESOLVE_DONE; - enum_value->type = decl->type; - } - return success; -} - -static inline bool sema_analyse_throws(Context *context, Decl *decl) -{ - if (!sema_resolve_type_info(context, decl->throws)) return false; - decl->type = decl->throws->type; - return true; -} - -static inline bool sema_analyse_error(Context *context, Decl *decl) -{ - Decl **constants = decl->error.error_constants; - unsigned size = vec_size(constants); - if (size > MAX_ERRORS) - { - SEMA_ERROR(decl, "More than %d errors declared in a single error type.", MAX_ERRORS); - return false; - } - bool success = true; - for (unsigned i = 0; i < size; i++) - { - Decl *constant = constants[i]; - for (unsigned j = 0; j < i; j++) - { - if (constant->name == constants[j]->name) - { - SEMA_ERROR(constant, "Duplicate error names, please remove one of them."); - SEMA_PREV(constants[j], "The previous declaration was here."); - decl_poison(constant); - decl_poison(constants[j]); - success = false; - break; - } - } - constant->error_constant.value = i; - constant->resolve_status = RESOLVE_DONE; - } - return success; -} - -bool sema_analyse_decl(Context *context, Decl *decl) -{ - if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); - - DEBUG_LOG("Analyse %s", decl->name); - if (decl->resolve_status == RESOLVE_RUNNING) - { - SEMA_ERROR(decl, "Recursive dependency on %s.", decl->name); - decl_poison(decl); - return false; - } - - decl->resolve_status = RESOLVE_RUNNING; - decl->module = context->module; - switch (decl->decl_kind) - { - case DECL_THROWS: - if (!sema_analyse_throws(context, decl)) return decl_poison(decl); - break; - case DECL_STRUCT: - case DECL_UNION: - if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); - llvm_set_struct_size_alignment(decl); - decl_set_external_name(decl); - break; - case DECL_FUNC: - if (!sema_analyse_func(context, decl)) return decl_poison(decl); - decl_set_external_name(decl); - break; - case DECL_MACRO: - if (!sema_analyse_macro(context, decl)) return decl_poison(decl); - break; - case DECL_VAR: - if (!sema_analyse_global(context, decl)) return decl_poison(decl); - decl_set_external_name(decl); - break; - case DECL_TYPEDEF: - if (!sema_analyse_typedef(context, decl)) return decl_poison(decl); - break; - case DECL_ENUM: - if (!sema_analyse_enum(context, decl)) return decl_poison(decl); - decl_set_external_name(decl); - break; - case DECL_ERROR: - if (!sema_analyse_error(context, decl)) return decl_poison(decl); - decl_set_external_name(decl); - break; - case DECL_GENERIC: - if (!sema_analyse_generic(context, decl)) return decl_poison(decl); - break; - case DECL_ATTRIBUTE: - TODO - case DECL_POISONED: - case DECL_IMPORT: - case DECL_ENUM_CONSTANT: - case DECL_ERROR_CONSTANT: - case DECL_ARRAY_VALUE: - case DECL_CT_ELSE: - case DECL_CT_ELIF: - UNREACHABLE - case DECL_CT_IF: - // Handled elsewhere - UNREACHABLE - } - decl->resolve_status = RESOLVE_DONE; - return true; -} - - -static void append_decls(Context *context, Decl **decls) -{ - VECEACH(decls, i) - { - context_register_global_decl(context, decls[i]); - } -} - -static inline bool sema_analyse_top_level_if(Context *context, Decl *ct_if) -{ - int res = sema_check_comp_time_bool(context, ct_if->ct_if_decl.expr); - if (res == -1) return false; - if (res) - { - append_decls(context, ct_if->ct_if_decl.then); - return true; - } - Decl *ct_elif = ct_if->ct_if_decl.elif; - while (ct_elif) - { - if (ct_elif->decl_kind == DECL_CT_ELIF) - { - res = sema_check_comp_time_bool(context, ct_elif->ct_elif_decl.expr); - if (res == -1) return false; - if (res) - { - append_decls(context, ct_elif->ct_elif_decl.then); - return true; - } - ct_elif = ct_elif->ct_elif_decl.elif; - } - else - { - assert(ct_elif->decl_kind == DECL_CT_ELSE); - append_decls(context, ct_elif->ct_elif_decl.then); - return true; - } - } - return true; -} - -void sema_analysis_pass_process_imports(Context *context) -{ - VECEACH(context->imports, i) - { - Decl *import = context->imports[i]; - import->resolve_status = RESOLVE_RUNNING; - // IMPROVE error on importing twice. - Path *path = import->import.path; - Module *module = stable_get(&compiler.modules, path->module); - if (!module) - { - SEMA_ERROR(import, "No module named '%s' could be found.", path->module); - decl_poison(import); - continue; - } - import->module = module; - } -} - -void sema_analysis_pass_conditional_compilation(Context *context) -{ - DEBUG_LOG("Pass 1 - analyse: %s", context->file->name); - for (unsigned i = 0; i < vec_size(context->ct_ifs); i++) - { - sema_analyse_top_level_if(context, context->ct_ifs[i]); - } -} - -void sema_analysis_pass_decls(Context *context) -{ - DEBUG_LOG("Pass 2 - analyse: %s", context->file->name); - VECEACH(context->enums, i) - { - sema_analyse_decl(context, context->enums[i]); - } - VECEACH(context->types, i) - { - sema_analyse_decl(context, context->types[i]); - } - VECEACH(context->error_types, i) - { - sema_analyse_decl(context, context->error_types[i]); - } - VECEACH(context->struct_functions, i) - { - sema_analyse_decl(context, context->struct_functions[i]); - } - VECEACH(context->vars, i) - { - sema_analyse_decl(context, context->vars[i]); - } - VECEACH(context->functions, i) - { - sema_analyse_decl(context, context->functions[i]); - } -} - - bool sema_resolve_type_info(Context *context, TypeInfo *type_info) { if (!sema_resolve_type_shallow(context, type_info)) return false; return true; } - -static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) -{ - Decl *ambiguous_decl; - Decl *decl = sema_resolve_symbol(context, - type_info->unresolved.name_loc.string, - type_info->unresolved.path, - &ambiguous_decl); - - if (!decl) - { - SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string); - return type_info_poison(type_info); - } - - // Already handled - if (!decl_ok(decl)) - { - return type_info_poison(type_info); - } - - - if (ambiguous_decl) - { - SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, - "Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", - type_info->unresolved.name_loc.string, - decl->module->name->module, - ambiguous_decl->module->name->module); - return type_info_poison(type_info); - } - switch (decl->decl_kind) - { - case DECL_THROWS: - TODO - case DECL_STRUCT: - case DECL_UNION: - case DECL_ERROR: - case DECL_ENUM: - type_info->type = decl->type; - type_info->resolve_status = RESOLVE_DONE; - DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string); - return true; - case DECL_TYPEDEF: - // TODO func - if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) - { - decl_poison(decl); - return type_info_poison(type_info); - } - DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string); - type_info->type = decl->type; - type_info->resolve_status = RESOLVE_DONE; - return true; - case DECL_POISONED: - return type_info_poison(type_info); - case DECL_FUNC: - case DECL_VAR: - case DECL_ENUM_CONSTANT: - case DECL_ERROR_CONSTANT: - case DECL_ARRAY_VALUE: - case DECL_IMPORT: - case DECL_MACRO: - case DECL_GENERIC: - SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "This is not a type."); - return type_info_poison(type_info); - case DECL_CT_ELSE: - case DECL_CT_IF: - case DECL_CT_ELIF: - case DECL_ATTRIBUTE: - UNREACHABLE - } - UNREACHABLE - -} - -bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info) -{ - if (type_info->resolve_status == RESOLVE_DONE) return type_info_ok(type_info); - - if (type_info->resolve_status == RESOLVE_RUNNING) - { - // TODO this is incorrect for unresolved expressions - SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, - "Circular dependency resolving type '%s'.", - type_info->unresolved.name_loc.string); - return type_info_poison(type_info); - } - - type_info->resolve_status = RESOLVE_RUNNING; - - switch (type_info->kind) - { - case TYPE_INFO_POISON: - case TYPE_INFO_INC_ARRAY: - UNREACHABLE - case TYPE_INFO_IDENTIFIER: - return sema_resolve_type_identifier(context, type_info); - case TYPE_INFO_EXPRESSION: - if (!sema_analyse_expr(context, NULL, type_info->unresolved_type_expr)) - { - return type_info_poison(type_info); - } - TODO - case TYPE_INFO_ARRAY: - return sema_resolve_array_type(context, type_info); - case TYPE_INFO_POINTER: - return sema_resolve_ptr_type(context, type_info); - } - -} \ No newline at end of file diff --git a/src/compiler/types.c b/src/compiler/types.c index d56ec3196..ba91af6b5 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -172,11 +172,12 @@ size_t type_size(Type *canonical) switch (canonical->type_kind) { case TYPE_POISONED: + case TYPE_TYPEDEF: UNREACHABLE; case TYPE_META_TYPE: return 0; case TYPE_ENUM: - case TYPE_TYPEDEF: + return type_size(canonical->decl->enums.type_info->type->canonical); case TYPE_STRUCT: case TYPE_UNION: case TYPE_ERROR: