Browse Source

Support `matrix` type in `core:odin`

gingerBill 3 years ago
parent
commit
90d587df13

+ 17 - 0
core/odin/ast/ast.odin

@@ -224,6 +224,15 @@ Slice_Expr :: struct {
 	close:    tokenizer.Pos,
 }
 
+Matrix_Index_Expr :: struct {
+	using node: Expr,
+	expr:         ^Expr,
+	open:         tokenizer.Pos,
+	row_index:    ^Expr,
+	column_index: ^Expr,
+	close:        tokenizer.Pos,
+}
+
 Call_Expr :: struct {
 	using node: Expr,
 	inlining: Proc_Inlining,
@@ -739,3 +748,11 @@ Relative_Type :: struct {
 	tag:  ^Expr,
 	type: ^Expr,
 }
+
+Matrix_Type :: struct {
+	using node: Expr,
+	tok_pos:      tokenizer.Pos,
+	row_count:    ^Expr,
+	column_count: ^Expr,
+	elem:         ^Expr,
+}

+ 8 - 1
core/odin/ast/clone.odin

@@ -117,6 +117,10 @@ clone_node :: proc(node: ^Node) -> ^Node {
 	case Index_Expr:
 		r.expr = clone(r.expr)
 		r.index = clone(r.index)
+	case Matrix_Index_Expr:
+		r.expr = clone(r.expr)
+		r.row_index = clone(r.row_index)
+		r.column_index = clone(r.column_index)
 	case Deref_Expr:
 		r.expr = clone(r.expr)
 	case Slice_Expr:
@@ -275,7 +279,10 @@ clone_node :: proc(node: ^Node) -> ^Node {
 	case Map_Type:
 		r.key = clone(r.key)
 		r.value = clone(r.value)
-
+	case Matrix_Type:
+		r.row_count = clone(r.row_count)
+		r.column_count = clone(r.column_count)
+		r.elem = clone(r.elem)
 	case:
 		fmt.panicf("Unhandled node kind: %T", r)
 	}

+ 8 - 0
core/odin/ast/walk.odin

@@ -110,6 +110,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 	case Index_Expr:
 		walk(v, n.expr)
 		walk(v, n.index)
+	case Matrix_Index_Expr:
+		walk(v, n.expr)
+		walk(v, n.row_index)
+		walk(v, n.column_index)
 	case Deref_Expr:
 		walk(v, n.expr)
 	case Slice_Expr:
@@ -398,6 +402,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 	case Relative_Type:
 		walk(v, n.tag)
 		walk(v, n.type)
+	case Matrix_Type:
+		walk(v, n.row_count)
+		walk(v, n.column_count)
+		walk(v, n.elem)
 
 	case:
 		fmt.panicf("ast.walk: unexpected node type %T", n)

+ 44 - 14
core/odin/parser/parser.odin

@@ -2703,6 +2703,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		bst.underlying = underlying
 		bst.close = close.pos
 		return bst
+		
+	case .Matrix:
+		tok := expect_token(p, .Matrix)
+		expect_token(p, .Open_Bracket)
+		row_count := parse_expr(p, false)
+		expect_token(p, .Comma)
+		column_count := parse_expr(p, false)
+		expect_token(p, .Close_Bracket)
+		elem := parse_type(p)
+
+		mt := ast.new(ast.Matrix_Type, tok.pos, elem.end)
+		mt.tok_pos = tok.pos
+		mt.row_count = row_count
+		mt.column_count = column_count
+		mt.elem = elem
+		return mt
 
 	case .Asm:
 		tok := expect_token(p, .Asm)
@@ -2969,7 +2985,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 			defer p.allow_range = prev_allow_range
 			p.allow_range = false
 
-			indicies: [2]^ast.Expr
+			indices: [2]^ast.Expr
 			interval: tokenizer.Token
 			is_slice_op := false
 
@@ -2981,18 +2997,18 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 				// NOTE(bill): Do not err yet
 				break
 			case:
-				indicies[0] = parse_expr(p, false)
+				indices[0] = parse_expr(p, false)
 			}
 
 			#partial switch p.curr_tok.kind {
 			case .Ellipsis, .Range_Half, .Range_Full:
 				error(p, p.curr_tok.pos, "expected a colon, not a range")
 				fallthrough
-			case .Colon:
+			case .Colon, .Comma/*matrix index*/:
 				interval = advance_token(p)
 				is_slice_op = true
 				if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
-					indicies[1] = parse_expr(p, false)
+					indices[1] = parse_expr(p, false)
 				}
 			}
 
@@ -3000,20 +3016,34 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 			p.expr_level -= 1
 
 			if is_slice_op {
-				se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close))
-				se.expr = operand
-				se.open = open.pos
-				se.low = indicies[0]
-				se.interval = interval
-				se.high = indicies[1]
-				se.close = close.pos
-
-				operand = se
+				if interval.kind == .Comma {
+					if indices[0] == nil || indices[1] == nil {
+						error(p, p.curr_tok.pos, "matrix index expressions require both row and column indices")
+					}
+					se := ast.new(ast.Matrix_Index_Expr, operand.pos, end_pos(close))
+					se.expr = operand
+					se.open = open.pos
+					se.row_index = indices[0]
+					se.column_index = indices[1]
+					se.close = close.pos
+
+					operand = se
+				} else {
+					se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close))
+					se.expr = operand
+					se.open = open.pos
+					se.low = indices[0]
+					se.interval = interval
+					se.high = indices[1]
+					se.close = close.pos
+
+					operand = se
+				}
 			} else {
 				ie := ast.new(ast.Index_Expr, operand.pos, end_pos(close))
 				ie.expr = operand
 				ie.open = open.pos
-				ie.index = indicies[0]
+				ie.index = indices[0]
 				ie.close = close.pos
 
 				operand = ie

+ 8 - 6
core/odin/tokenizer/token.odin

@@ -150,6 +150,7 @@ Token_Kind :: enum u32 {
 		Asm,         // asm
 		Inline,      // inline
 		No_Inline,   // no_inline
+		Matrix,      // matrix
 	B_Keyword_End,
 
 	COUNT,
@@ -280,6 +281,7 @@ tokens := [Token_Kind.COUNT]string {
 	"asm",
 	"inline",
 	"no_inline",
+	"matrix",
 	"",
 }
 
@@ -299,10 +301,10 @@ token_to_string :: proc(tok: Token) -> string {
 }
 
 to_string :: proc(kind: Token_Kind) -> string {
-	if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
+	if .Invalid <= kind && kind < .COUNT {
 		return tokens[kind]
 	}
-	if Token_Kind.B_Custom_Keyword_Begin < kind {
+	if .B_Custom_Keyword_Begin < kind {
 		n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin))
 		if n < len(custom_keyword_tokens) {
 			return custom_keyword_tokens[n]
@@ -313,7 +315,7 @@ to_string :: proc(kind: Token_Kind) -> string {
 }
 
 is_literal  :: proc(kind: Token_Kind) -> bool {
-	return Token_Kind.B_Literal_Begin  < kind && kind < Token_Kind.B_Literal_End
+	return .B_Literal_Begin  < kind && kind < .B_Literal_End
 }
 is_operator :: proc(kind: Token_Kind) -> bool {
 	#partial switch kind {
@@ -327,13 +329,13 @@ is_operator :: proc(kind: Token_Kind) -> bool {
 	return false
 }
 is_assignment_operator :: proc(kind: Token_Kind) -> bool {
-	return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq
+	return .B_Assign_Op_Begin < kind && kind < .B_Assign_Op_End || kind == .Eq
 }
 is_keyword :: proc(kind: Token_Kind) -> bool {
 	switch {
-	case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
+	case .B_Keyword_Begin < kind && kind < .B_Keyword_End:
 		return true
-	case Token_Kind.B_Custom_Keyword_Begin < kind:
+	case .B_Custom_Keyword_Begin < kind:
 		return true
 	}
 	return false