// this file generates ../infix.v
module main

import strings
import os

const header = '[generated]
module eval
import v.token
import v.ast
fn(e Eval)infix_expr(left Object,right Object,op token.Kind,expecting ast.Type)Object{match op{'

const footer = "else{e.error('unknown infix expression: \$op')}}return empty // should e.error before this anyway
}
"

const uk_expect_footer = "else{e.error('unknown infix expectation: \${e.table.sym(expecting).str()}')}}"
const comparison = {
	'gt': '>'
	'lt': '<'
	'eq': '=='
	'ne': '!='
}
const math_ops = {
	'plus':        '+'
	'minus':       '-'
	'mul':         '*'
	'div':         '/'
	'right_shift': '>>'
	'left_shift':  '<<'
}
const compound_types = ['Int', 'Uint', 'Float']
const literal_types = ['i64', 'f64']

fn main() {
	mut b := strings.new_builder(124000)
	b.write_string(header)

	for enm, op in comparison {
		b.write_string('.${enm}{match left{')
		for ct in compound_types {
			b.write_string('${ct} {match right{')
			for ct2 in compound_types {
				b.write_string('${ct2}{return left.val${op}right.val}')
			}
			for lt2 in literal_types {
				b.write_string('${lt2}{return left.val${op}right}')
			}
			b.write_string("else{e.error('invalid operands to ${op}: ${ct} and \$right.type_name()')}}}")
		}
		for lt in literal_types {
			b.write_string('${lt} {match right{')
			for ct2 in compound_types {
				b.write_string('${ct2}{return left${op}right.val}')
			}
			for lt2 in literal_types {
				b.write_string('${lt2}{return left${op}right}')
			}
			b.write_string("else {e.error('invalid operands to ${op}: ")
			b.write_string(if lt == 'i64' { 'int' } else { 'float' })
			b.write_string(" literal and \$right.type_name()')}}}")
		}
		if op in ['==', '!='] {
			b.write_string('string{match right{string{return left${op}right}else{e.error(\'invalid operands to ${op}: string and \$right.type_name()\')}}}')
		}
		b.write_string("else {e.error('invalid operands to ${op}: \$left.type_name() and \$right.type_name()')}}}")
	}
	for math, op in math_ops {
		b.write_string('.${math}{match left{')
		for ct in compound_types {
			if op in ['<<', '>>'] && ct == 'Float' {
				continue
			}
			b.write_string('${ct} {match right{')
			for ct2 in compound_types {
				if op in ['<<', '>>'] && ct2 == 'Float' {
					continue
				}
				unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
				b.write_string('${ct2}{if expecting in ast.signed_integer_type_idxs{return Int{i64(left.val)${op}i64(right.val),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left.val)${op}u64(right.val),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left.val)${op}i64(right.val))${unsafe_end}}')
				if op !in ['<<', '>>'] {
					b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left.val)${op}f64(right.val), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left.val)${op}f64(right.val))}')
				}
				b.write_string(uk_expect_footer)
			}
			for lt2 in literal_types {
				if op in ['<<', '>>'] && lt2 == 'f64' {
					continue
				}
				unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
				b.write_string('${lt2}{if expecting in ast.signed_integer_type_idxs{return Int{i64(left.val)${op}i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left.val)${op}u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left.val)${op}i64(right))${unsafe_end}}')
				if op !in ['<<', '>>'] {
					b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left.val)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left.val)${op}f64(right))}')
				}
				b.write_string(uk_expect_footer)
			}
			b.write_string("else {e.error('invalid operands to ${op}: ${ct} and \$right.type_name()')}}}")
		}
		for lt in literal_types {
			if op in ['<<', '>>'] && lt == 'f64' {
				continue
			}
			b.write_string('${lt}{match right{')
			for ct2 in compound_types {
				if op in ['<<', '>>'] && ct2 == 'Float' {
					continue
				}
				unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
				b.write_string('${ct2}{if expecting in ast.signed_integer_type_idxs{return Int{i64(left)${op}i64(right.val),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left)${op}u64(right.val),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right.val))${unsafe_end}}')
				if op !in ['<<', '>>'] {
					b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left)${op}f64(right.val), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left)${op}f64(right.val))}')
				}
				b.write_string(uk_expect_footer)
			}
			for lt2 in literal_types {
				if op in ['<<', '>>'] && lt2 == 'f64' {
					continue
				}
				unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
				b.write_string('${lt2}{if expecting in ast.signed_integer_type_idxs{return Int{i64(left)${op}i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left)${op}u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right))${unsafe_end}}')
				if op !in ['<<', '>>'] {
					b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left)${op}f64(right))}')
				}
				b.write_string(uk_expect_footer)
			}
			b.write_string("else {e.error('invalid operands to ${op}: ")
			b.write_string(if lt == 'i64' { 'int' } else { 'float' })
			b.write_string(" literal and \$right.type_name()')}}}")
		}
		b.write_string("else {e.error('invalid operands to ${op}: \$left.type_name() and \$right.type_name()')}}}")
	}

	b.write_string(footer)

	path := @FILE.all_before(@FILE.all_after_last('/')) + '../infix.v'
	os.write_file(path, b.str()) or { panic(err) }
	res := os.execute(os.quoted_path(@VEXE) + ' fmt -w ' + os.quoted_path(path))
	if res.exit_code != 0 {
		eprintln('v fmt failed!')
		panic(res.output)
	}
}
