diff --git a/ir.v b/ir.v index 62be94e..ff62efc 100644 --- a/ir.v +++ b/ir.v @@ -42,7 +42,7 @@ pub mut: refs []IRRef refs_map map[RID]VID vars_map map[string]VID - operand []IROperand + consts []IRConstant entrybb BBID is_inline bool // Later inline_defaults []int // Later @@ -74,6 +74,8 @@ pub mut: stmts []Stmt // The list of statements from the IR that form this basic block } +pub type BBAID = int + pub struct IRBasicBlockArg { pub mut: name string @@ -83,7 +85,12 @@ pub mut: } type IID = int -pub type IRInstruction = IRMacroLiteralCmd | IRDefine | IRTypedDefine | IRBinaryOp | IRUnaryOp +pub type IRInstruction = IRMacroLiteralCmd + | IRDefine + | IRTypedDefine + | IRBinaryOp + | IRUnaryOp + | IRAssign pub struct IRDefine { pub mut: @@ -98,14 +105,55 @@ pub mut: result RID } +pub fn (t TokenKind) to_assign_op() AssignOp { + return match t { + .assign { .assign } + .plus_assign { .add_assign } + .minus_assign { .sub_assign } + .star_assign { .mul_assign } + .slash_assign { .div_assign } + .percent_assign { .mod_assign } + .swap { .swap } + else { panic('illegal use of token ${t} as a Assign operation') } + } +} + pub enum AssignOp { assign - add - sub - mul - div - mod + add_assign + sub_assign + mul_assign + div_assign + mod_assign + swap +} + +pub fn (a AssignOp) print() { + match a { + .assign { + print('=') + } + .add_assign { + print('+=') + } + .sub_assign { + print('-=') + } + .mul_assign { + print('*=') + } + .div_assign { + print('/=') + } + .mod_assign { + print('%=') + } + .swap { + print('><') + } + } } + pub struct IRAssign { pub mut: id IID @@ -252,6 +300,7 @@ pub struct IRJump { pub mut: id IID target BBID + // TODO: add value here for calling the .mcfunction with args } pub struct IRBranch { @@ -260,6 +309,7 @@ pub mut: cond OID then BBID el BBID + // TODO: add values here for calling the .mcfunction with args } pub struct IRReturn { @@ -304,6 +354,7 @@ pub mut: text string } +// TODO: make it so that all string macros can be any expression just inside of a macro pub struct IRStringMacro { pub mut: value RID @@ -355,14 +406,50 @@ pub struct IRRef { pub mut: id RID value IRRefSum + typ IRType } // IR OPERAND -type OID = int -pub type IROperand = RID | IRConstant +type OID = RID | CID +@[inline] +pub fn (id OID) to_ir_type(f IRFunction) IRType { + match id { + RID { + return f.refs[id].typ + } + CID { + match f.consts[id] { + IRIntConst { + return BuiltinType.int_t + } + IRCharConst { + return BuiltinType.char_t + } + IRFloatConst { + return BuiltinType.float_t + } + IRStringConst { + return BuiltinType.string_t + } + IRListConst { + return BuiltinType.list_t + } + IRDictConst { + return BuiltinType.dict_t + } + else { + panic('Type not implemented in oid to ir type ${f.consts[id]}') + } + } + } + } +} + +type CID = int pub type IRConstant = IRIntConst | IRFloatConst + | IRCharConst | IRStringConst | IRListConst | IRDictConst @@ -373,6 +460,11 @@ pub mut: value int } +pub struct IRCharConst { +pub mut: + value string +} + pub struct IRFloatConst { pub mut: value f64 @@ -380,7 +472,6 @@ pub mut: pub struct IRStringConst { pub mut: - value string parts []IRStringPart // For interpolated strings } @@ -850,6 +941,9 @@ pub fn (mut b IRBuilder) literal_has_macro(l Literal) bool { } } } + FloatLiteral { + return false + } CharLiteral { return false } @@ -900,6 +994,9 @@ pub fn (mut b IRBuilder) literal_needs_block(l Literal) bool { IntegerLiteral { return false } + FloatLiteral { + return false + } StringLiteral { if !l.interpolated { return false diff --git a/ir_instruction_gen.v b/ir_instruction_gen.v index 3368679..28c1702 100644 --- a/ir_instruction_gen.v +++ b/ir_instruction_gen.v @@ -2,9 +2,8 @@ module main import datatypes -pub fn (mut b IRBuilder) bb_get_reference(name string, - bbid BBID, - fid FID) (bool, IRRef) { +// gets it and ads it to the local map +pub fn (mut b IRBuilder) bb_get_reference(name string, bbid BBID, fid FID) (bool, IRRef) { mut bb := b.basic_blocks[bbid] mut fun := b.functions[fid] mut ns := b.namespaces[fun.namespace] @@ -18,6 +17,7 @@ pub fn (mut b IRBuilder) bb_get_reference(name string, ref := IRRef{ id: fun.refs.len value: arg + typ: arg.typ } fun.refs << ref bb.vars_map[name] = ref.id @@ -77,20 +77,109 @@ pub fn (mut b IRBuilder) bb_get_reference(name string, return false, IRRef{} } +pub enum RefLevel { + invalid + bb_args + pred_block + pred_args + fun_args + namespace +} + +// Just looks for it does not add it anywhere +pub fn (mut b IRBuilder) bb_get_reference_no_creation(name string, bbid BBID, fid FID) (bool, IRRef, RefLevel) { + mut bb := b.basic_blocks[bbid] + mut fun := b.functions[fid] + mut ns := b.namespaces[fun.namespace] + + // check if its in our arguments + for arg in bb.args { + if arg.name == name { + ref := IRRef{ + id: RID(-1) + value: arg + typ: arg.typ + } + return true, ref, RefLevel.bb_args + } + } + + mut visited := map[BBID]bool{} + visited[bbid] = true + mut to_visit := datatypes.Queue[BBID]{} + for pred in bb.predecessors { + to_visit.push(pred) + } + + // check if its defined in any predecesor blocks + // who exist in the same scope TODO + for !to_visit.is_empty() { + new_id := to_visit.pop() or { panic("we really thought we'd have something lol") } + if new_id in visited { + continue + } + new := b.basic_blocks[new_id] + if name in new.vars_map { + ref := new.vars_map[name] + return true, fun.refs[ref], RefLevel.pred_block + } + + for arg in new.args { + if arg.name == name { + ref := IRRef{ + id: RID(-1) + value: arg + typ: arg.typ + } + return true, ref, RefLevel.pred_args + } + } + + visited[new_id] = true + for pred in new.predecessors { + to_visit.push(pred) + } + } + + // check if its in our function's arguments + for arg in fun.args { + if arg.name == name { + ref := IRRef{ + id: RID(-1) + value: arg + } + return true, ref, RefLevel.fun_args + } + } + + // check if its in our owning namespace + if name in ns.variable_map { + ref := IRRef{ + id: RID(-1) + value: IRRefSum(ns.variable_map[name]) + } + return true, ref, RefLevel.namespace + } + + return false, IRRef{}, RefLevel.invalid +} + pub fn (mut b IRBuilder) bb_add_reference(bbid BBID, vid VID) RID { ref := IRRef{ id: b.functions[b.basic_blocks[bbid].function].refs.len value: IRRefSum(vid) + typ: b.variables[vid].typ } b.functions[b.basic_blocks[bbid].function].refs << ref b.basic_blocks[bbid].vars_map[b.variables[vid].name] = ref.id return ref.id } -pub fn (mut b IRBuilder) bb_add_anon_reference(bbid BBID, iid IID) RID { +pub fn (mut b IRBuilder) bb_add_anon_reference(bbid BBID, iid IID, typ IRType) RID { ref := IRRef{ id: b.functions[b.basic_blocks[bbid].function].refs.len value: IRRefSum(iid) + typ: typ } b.functions[b.basic_blocks[bbid].function].refs << ref return ref.id @@ -100,8 +189,41 @@ pub fn (mut b IRBuilder) bb_add_anon_reference(bbid BBID, iid IID) RID { // bb := b.basic_blocks[bbid] // func := b.function[fid] // } +pub fn (mut b IRBuilder) bb_add_value_ir(bbid BBID, name string, typ IRType, source ValueType) VID { + bb := b.basic_blocks[bbid] + fid := bb.function + func := b.functions[fid] + ns := b.namespaces[func.namespace] + mut val := IRValue{ + name: name + id: b.variables.len + typ: typ + storage: source.to_ir() + location: match source { + .effemeral { + IRLocation(IREffLocation{ + value: name + }) + } + .data { + IRLocation(IRDataLocation{ + namespace: ns.id + function: fid + }) + } + .register { + IRLocation(IRRegLocation{ + namespace: ns.id + function: fid + }) + } + } + } + b.variables << val + return val.id +} -pub fn (mut b IRBuilder) bb_add_value(bbid BBID, name string, typ Type, source ValueType) VID { +pub fn (mut b IRBuilder) bb_add_value_ast(bbid BBID, name string, typ Type, source ValueType) VID { bb := b.basic_blocks[bbid] fid := bb.function func := b.functions[fid] @@ -137,6 +259,69 @@ pub fn (mut b IRBuilder) bb_add_value(bbid BBID, name string, typ Type, source V return val.id } +// returns if its a refered macro, and the RID for accessing it! +pub fn (mut b IRBuilder) bb_handle_macro_expr(bbid BBID, mexpr MacroExpr) (bool, RID) { + refed := mexpr.referable + fid := b.basic_blocks[bbid].function + // TODO: Implement going down the identifier chain -> will have to make macro expasions and such.... + // which will create more basic blocks. + chain := mexpr.ident_chain + if chain.elements.len > 1 { + panic('Not implemented identifier chains yet...') + } + e := chain.elements[0] + match e { + IdentifierChainName { + ok, ref := b.bb_get_reference(e.name.name, bbid, fid) + if !ok { + panic('could not find value to macro expand in ${mexpr}') + } + + match ref.value { + IRBasicBlockArg { + if ref.value.id == bbid { + // we don't have to add it as something that we want, someone has already done this for us. + return refed, ref.id + } + } + else {} + } + + // It is not an argument to ourself... We need to ask for this thing. + // As we are going to be using it as a macro value it has to be as a data -> we can set the storage type to data + // type IRRefSum = VID | IRBasicBlockArg | IRFunctionArg | IID + mut arg := IRBasicBlockArg{ + id: bbid + typ: ref.typ + storage: .data + } + match ref.value { + IRBasicBlockArg { + arg.name = ref.value.name + } + IRFunctionArg { + arg.name = ref.value.name + } + VID { + arg.name = b.variables[ref.value].name + } + IID { + panic('should be impossible for for an instruction to be the reference for a name...') + arg.name = '' + } + } + b.basic_blocks[bbid].args << arg + return refed, ref.id + } + else { + panic('not implemented anything other than identiferi chain name for macroexpr ${e}') + } + } + + panic('could not handle macro expr') + return refed, RID(-1) +} + // Returns an oid that we can set to something, later on we can merge the instructions together pub fn (mut b IRBuilder) lower_expression(bbid BBID, fid FID, expr Expr) OID { bb := b.basic_blocks[bbid] @@ -150,46 +335,118 @@ pub fn (mut b IRBuilder) lower_expression(bbid BBID, fid FID, expr Expr) OID { right: b.lower_expression(bbid, fid, expr.right) } inst.id = b.functions[fid].insts.len - inst.result = b.bb_add_anon_reference(bbid, inst.id) + inst.result = b.bb_add_anon_reference(bbid, inst.id, inst.left.to_ir_type(func)) b.functions[fid].insts << inst b.basic_blocks[bbid].insts << inst.id - b.functions[fid].operand << IROperand(inst.result) - return OID(b.functions[fid].operand.len - 1) + return OID(inst.result) } UnaryExpr { + // Will become not a literal, therefore we can have it not be a constant anymore mut inst := IRUnaryOp{ op: expr.operator.to_unary_op() operand: b.lower_expression(bbid, fid, expr.right) } inst.id = b.functions[fid].insts.len - inst.result = b.bb_add_anon_reference(bbid, inst.id) + inst.result = b.bb_add_anon_reference(bbid, inst.id, inst.operand.to_ir_type(func)) b.functions[fid].insts << inst b.basic_blocks[bbid].insts << inst.id - b.functions[fid].operand << IROperand(inst.result) - return OID(b.functions[fid].operand.len - 1) + return OID(inst.result) + } + Literal { + lit := expr + match lit { + IntegerLiteral { + cid := CID(b.functions[fid].consts.len) + b.functions[fid].consts << IRIntConst{ + value: lit.value + } + return OID(cid) + } + CharLiteral { + cid := CID(b.functions[fid].consts.len) + b.functions[fid].consts << IRCharConst{ + value: lit.value + } + return OID(cid) + } + FloatLiteral { + cid := CID(b.functions[fid].consts.len) + b.functions[fid].consts << IRFloatConst{ + value: lit.value + } + return OID(cid) + } + StringLiteral { + mut ir_str := IRStringConst{} + if lit.value != '' { + ir_str.parts << IRStringText{ + text: lit.value + } + } + if lit.interpolated { + for part in lit.parts { + if !part.is_macro { + ir_str.parts << IRStringText{ + text: part.text + } + continue + } + // We have a macro that we have to handle here... + refed, mrid := b.bb_handle_macro_expr(bbid, part.macro_expr or { + panic('should be a macro expression in this string literal???') + }) + mcro := IRStringMacro{ + value: mrid + is_ref: refed + } + ir_str.parts << mcro + } + } + cid := CID(b.functions[fid].consts.len) + b.functions[fid].consts << ir_str + return OID(cid) + } + ListLiteral { + panic('list literal not implemented') + } + DictionaryLiteral { + panic('dictionary literal not implemented') + } + RangeLiteral { + panic('range literal not implemented') + } + } } Identifier { // Will have to be a ref, and it must exist! otherwise we're boned boys ok, ref := b.bb_get_reference(expr.name, bbid, fid) if ok { - b.functions[fid].operand << IROperand(ref.id) - return OID(b.functions[fid].operand.len - 1) + return OID(ref.id) } else { panic('We could not find a reference to the identifier ${expr}') } } - Literal {} - MacroExpr {} - AccessExpr {} - StructLiteral {} - QualifiedIdentifier {} + MacroExpr { + panic('not implemented macro expr yet for lowering expressoin') + } + AccessExpr { + panic('not implemente d access expressoin yet for loweing expression') + } + StructLiteral { + panic('not implemented struct literal expr yet for loweing expressions') + } + QualifiedIdentifier { + panic('not yet implemented qualified identifier lowering for loweriing expresions yet') + } } - return OID(-1) + + panic('could not lower expression') + return OID(CID(-1)) } pub fn (mut b IRBuilder) fill_bb_insts_stmt(bbid BBID, fid FID, stmt Stmt) { - bb := b.basic_blocks[bbid] - func := b.functions[fid] - ns := b.namespaces[func.namespace] + mut bb := b.basic_blocks[bbid] + mut func := b.functions[fid] + mut ns := b.namespaces[func.namespace] match stmt { TypedDefine { ok, ref := b.bb_get_reference(stmt.name.name, bbid, fid) @@ -197,17 +454,11 @@ pub fn (mut b IRBuilder) fill_bb_insts_stmt(bbid BBID, fid FID, stmt Stmt) { // This should not be allowed.... We are not allowed to have // this sort of shadowing! panic('When defining ${stmt} we found a reference to it already existing.') - // inst := IRTypedDefine{ - // id: func.insts.len - // result: ref.id - // } - // b.functions[fid].insts << inst - // b.basic_blocks[bbid].insts << inst.id } else { // No reference to this exists so we should like make one // need to create a value inst := IRTypedDefine{ id: func.insts.len - result: b.bb_add_reference(bbid, b.bb_add_value(bbid, stmt.name.name, + result: b.bb_add_reference(bbid, b.bb_add_value_ast(bbid, stmt.name.name, stmt.typ, stmt.source)) } b.functions[fid].insts << inst @@ -222,7 +473,55 @@ pub fn (mut b IRBuilder) fill_bb_insts_stmt(bbid BBID, fid FID, stmt Stmt) { } else { // We are the first creation point for this thing! // We have to first figure out the type of the expression that we are coming from... // And generate the instructions that compose this expression... + mut inst := IRDefine{ + value: b.lower_expression(bbid, fid, stmt.value) + } + inst.result = b.bb_add_reference(bbid, b.bb_add_value_ir(bbid, stmt.name.name, + inst.value.to_ir_type(b.functions[fid]), stmt.source)) + inst.id = b.functions[fid].insts.len + b.functions[fid].insts << inst + b.basic_blocks[bbid].insts << inst.id + } + } + Assignment { + op := stmt.operator.to_assign_op() + left_oid := b.lower_expression(bbid, fid, stmt.left) + left := match left_oid { + CID { + panic('Cannot assign to a constant value') + RID(-1) + } + RID { + left_oid + } } + + right := b.lower_expression(bbid, fid, stmt.right) + match op { + .assign { + // Check that left and right are same type. + if OID(left).to_ir_type(b.functions[fid]) != right.to_ir_type(b.functions[fid]) { + panic('cannot assign accross types') + } + // TODO: handle checking if the sources are different to + } + else { + lref := b.functions[fid].refs[left] + if lref.typ is BuiltinType { + if lref.typ != BuiltinType.int_t { + panic('cannot do operation ${op} onto type ${lref.typ}') + } + } + } + } + mut inst := IRAssign{ + id: b.functions[fid].insts.len + result: left + op: op + value: right + } + b.functions[fid].insts << inst + b.basic_blocks[bbid].insts << inst.id } else { println('not implemented ${stmt}') diff --git a/lx.v b/lx.v index 12797bb..c98096a 100644 --- a/lx.v +++ b/lx.v @@ -6,6 +6,7 @@ enum TokenKind { invalid lit_string lit_integer + lit_float lit_char eof ident @@ -154,6 +155,23 @@ pub fn (mut l Lexer) read_integer() Token { } } +pub fn (mut l Lexer) read_integer_or_float() Token { + start := l.index + first := l.read_integer() + if (l.peek() == `.`) { + l.index += 1 + second := l.read_integer() + + str := l.src[start..l.index] + return Token{ + kind: .lit_float + str: str + pos: start + } + } + return first +} + pub fn (mut l Lexer) read_string() Token { start := l.index mut counter := 0 @@ -491,7 +509,7 @@ pub fn (mut l Lexer) next_token() Token { return l.read_identifier() } is_digit(p) { - return l.read_integer() + return l.read_integer_or_float() } else { start := l.index diff --git a/main.v b/main.v index 5984c07..8699681 100644 --- a/main.v +++ b/main.v @@ -2,11 +2,19 @@ module main fn main() { mut irb := IRBuilder{} - irb.lower(['tests/ir/bb_gen/stmts/typed_define.mcf'])! + irb.lower(['tests/ir/bb_gen/stmts/assign.mcf'])! irb.print_cfg_dot() - for bb in irb.basic_blocks { + for mut bb in irb.basic_blocks { irb.fill_bb_insts(bb.id) print(irb.functions[bb.function].insts) + print('\n') + print(bb.insts) + print('\n') + for inst in bb.insts { + irb.fn_print_inst(bb.function, inst) + print('\n') + } + // print(irb.functions[bb.function].block) } // for func in irb.functions { diff --git a/parse.v b/parse.v index eb8381a..73adeb8 100644 --- a/parse.v +++ b/parse.v @@ -61,6 +61,7 @@ pub type Literal = IntegerLiteral | ListLiteral | DictionaryLiteral | RangeLiteral + | FloatLiteral pub type AccessExpr = MemberAccessExpr | IndexAccessExpr | FunctionCallExpr @@ -277,6 +278,12 @@ mut: value string } +pub struct FloatLiteral { + Node +mut: + value f32 +} + pub struct ListLiteral { Node mut: @@ -318,6 +325,7 @@ mut: name string } +// TODO: make macros that can belong in strings handle any expression pub struct MacroExpr { Node mut: @@ -426,8 +434,9 @@ pub type Type = BuiltinType | ReferenceType | StructType pub enum BuiltinType { int_t float_t - list_t + char_t string_t + list_t dict_t void_t } @@ -1793,8 +1802,9 @@ fn (mut p Parser) parse_statement() Stmt { match p.current.kind { .assign, .plus_assign, .minus_assign, .star_assign, .slash_assign, .percent_assign, - .eq, .ne, .lte, .gte, .lcarrot, .rcarrot, .swap, .plus, .minus, .star, .slash, + .swap, .eq, .ne, .lte, .gte, .lcarrot, .rcarrot, .plus, .minus, .star, .slash, .percent { + // TODO: check if we rip this out will it still work well.... because like ermmm no? op := p.current.kind p.advance() right := p.parse_expr(0) diff --git a/tests/ir/bb_gen/stmts/assign.mcf b/tests/ir/bb_gen/stmts/assign.mcf new file mode 100644 index 0000000..aecc771 --- /dev/null +++ b/tests/ir/bb_gen/stmts/assign.mcf @@ -0,0 +1,6 @@ +fn a() : Void{ + data b : Int; + data c := 10; + b = 10; + c = c + b; +} diff --git a/tests/ir/bb_gen/stmts/define.mcf b/tests/ir/bb_gen/stmts/define.mcf new file mode 100644 index 0000000..4714f1d --- /dev/null +++ b/tests/ir/bb_gen/stmts/define.mcf @@ -0,0 +1,4 @@ +fn a() : Void{ + data b : Int; + data c := b; +} diff --git a/utils_ir.v b/utils_ir.v index 2da5a19..10fd013 100644 --- a/utils_ir.v +++ b/utils_ir.v @@ -124,10 +124,95 @@ pub fn (mut b IRBuilder) print_ir_location(location IRLocation) { pub fn (mut b IRBuilder) fn_print_rid(fid FID, rid RID) { func := b.functions[fid] ref := func.refs[rid] - match ref.value { - VID {} + print('(${ref.typ} ') + v := ref.value + match v { + VID { + b.print_ir_location(b.variables[v].location) + print(').${b.variables[v].name} ') + } IRBasicBlockArg {} IRFunctionArg {} - IID {} + IID { + print(b.namespaces[func.namespace].name) + print(':') + print(func.name) + print(':inst_${v}') + } + } +} + +pub fn (mut b IRBuilder) fn_print_oid(fid FID, id OID) { + func := b.functions[fid] + match id { + CID { + c := b.functions[fid].consts[id] + match c { + IRFloatConst { + print('${c.value}') + } + IRIntConst { + print('${c.value}') + } + IRCharConst { + print('${c.value}') + } + IRStringConst { + for part in c.parts { + match part { + IRStringText { + print(part.text) + } + IRStringMacro { + if part.is_ref { + print('&') + } + b.fn_print_rid(fid, part.value) + } + } + } + } + else { + print('const ${c} not yet supported') + } + } + } + RID { + b.fn_print_rid(fid, id) + } + } +} + +pub fn (mut b IRBuilder) fn_print_inst(fid FID, iid IID) { + func := b.functions[fid] + inst := func.insts[iid] + match inst { + IRTypedDefine { + print('define ') + b.fn_print_rid(fid, inst.result) + } + IRDefine { + print('define ') + b.fn_print_rid(fid, inst.result) + print(' = ') + b.fn_print_oid(fid, inst.value) + } + IRAssign { + print('assign ') + b.fn_print_rid(fid, inst.result) + inst.op.print() + b.fn_print_oid(fid, inst.value) + } + IRBinaryOp { + print('binop ') + b.fn_print_rid(fid, inst.result) + print(' = ') + b.fn_print_oid(fid, inst.left) + print(inst.op) + b.fn_print_oid(fid, inst.right) + } + else { + print('inst is not supported yes ${inst}') + } } }