diff --git a/microcontroller/core/include/c-runtime.h b/microcontroller/core/include/c-runtime.h index f6e306e..285f0e7 100644 --- a/microcontroller/core/include/c-runtime.h +++ b/microcontroller/core/include/c-runtime.h @@ -131,6 +131,7 @@ extern value_t CR_SECTION any_add(value_t a, value_t b); extern value_t CR_SECTION any_subtract(value_t a, value_t b); extern value_t CR_SECTION any_multiply(value_t a, value_t b); extern value_t CR_SECTION any_divide(value_t a, value_t b); +extern value_t CR_SECTION any_modulo(value_t a, value_t b); extern bool CR_SECTION any_less(value_t a, value_t b); extern bool CR_SECTION any_less_eq(value_t a, value_t b); @@ -141,6 +142,7 @@ extern value_t CR_SECTION any_add_assign(value_t* a, value_t b); extern value_t CR_SECTION any_subtract_assign(value_t* a, value_t b); extern value_t CR_SECTION any_multiply_assign(value_t* a, value_t b); extern value_t CR_SECTION any_divide_assign(value_t* a, value_t b); +extern value_t CR_SECTION any_modulo_assign(value_t* a, value_t b); extern value_t CR_SECTION any_increment(value_t* expr); extern value_t CR_SECTION any_decrement(value_t* expr); diff --git a/microcontroller/core/src/c-runtime.c b/microcontroller/core/src/c-runtime.c index b3e1fb4..ed3d014 100644 --- a/microcontroller/core/src/c-runtime.c +++ b/microcontroller/core/src/c-runtime.c @@ -279,6 +279,14 @@ ANY_OP_FUNC(subtract,-) ANY_OP_FUNC(multiply,*) ANY_OP_FUNC(divide,/) +value_t any_modulo(value_t a, value_t b) { + if (is_int_value(a)) + if (is_int_value(b)) + return int_to_value(value_to_int(a) % value_to_int(b)); + + return runtime_type_error("bad operand for %%"); +} + #define ANY_CMP_FUNC(name, op) \ bool any_##name(value_t a, value_t b) {\ if (is_int_value(a)) {\ @@ -323,6 +331,14 @@ ANY_ASSIGN_OP_FUNC(subtract,-) ANY_ASSIGN_OP_FUNC(multiply,*) ANY_ASSIGN_OP_FUNC(divide,/) +value_t any_modulo_assign(value_t* a, value_t b) { + if (is_int_value(*a)) + if (is_int_value(b)) + return *a = int_to_value(value_to_int(*a) % value_to_int(b)); + + return runtime_type_error("bad operand for %%="); +} + #define ANY_UPDATE(name, op, code) \ value_t any_##name(value_t* expr) {\ value_t v;\ diff --git a/server/src/transpiler/code-generator/c-runtime.ts b/server/src/transpiler/code-generator/c-runtime.ts index 61d82a6..a793f80 100644 --- a/server/src/transpiler/code-generator/c-runtime.ts +++ b/server/src/transpiler/code-generator/c-runtime.ts @@ -203,6 +203,8 @@ export function arithmeticOpForAny(op: string) { return 'any_multiply' case '/': return 'any_divide' + case '%': + return 'any_modulo' case '+=': return 'any_add_assign' case '-=': @@ -211,6 +213,8 @@ export function arithmeticOpForAny(op: string) { return 'any_multiply_assign' case '/=': return 'any_divide_assign' + case '%=': + return 'any_modulo_assign' case 'i': // ++v return 'any_increment' case 'p': // v++ diff --git a/server/src/transpiler/code-generator/code-generator.ts b/server/src/transpiler/code-generator/code-generator.ts index 20a9879..7a03364 100644 --- a/server/src/transpiler/code-generator/code-generator.ts +++ b/server/src/transpiler/code-generator/code-generator.ts @@ -862,9 +862,9 @@ export class CodeGenerator extends visitor.NodeVisitor { if (op === '==' || op === '!=' || op === '===' || op === '!==') this.equalityExpression(op, left, right, env) else if (op === '<' || op === '<=' || op === '>' || op === '>=' - || op === '+' || op === '-' || op === '*' || op === '/') + || op === '+' || op === '-' || op === '*' || op === '/' || op === '%') this.basicBinaryExpression(op, left, right, env) - else if (op === '|' || op === '^' || op === '&' || op === '%' || op === '<<' || op === '>>') { + else if (op === '|' || op === '^' || op === '&' || op === '<<' || op === '>>') { // both left and right are integer or float. this.numericBinaryExprssion(op, left, right, env) } @@ -903,7 +903,7 @@ export class CodeGenerator extends visitor.NodeVisitor { this.numericBinaryExprssion(op2, left, right, env) } - // +, -, *, /, <, <=, ... for integer, float, or any-type values + // +, -, *, /, %, <, <=, ... for integer, float, or any-type values private basicBinaryExpression(op: string, left: AST.Node, right: AST.Node, env: VariableEnv): void { const left_type = this.needsCoercion(left) const right_type = this.needsCoercion(right) diff --git a/server/src/transpiler/type-checker.ts b/server/src/transpiler/type-checker.ts index 0c2a95d..ea46342 100644 --- a/server/src/transpiler/type-checker.ts +++ b/server/src/transpiler/type-checker.ts @@ -675,7 +675,18 @@ export default class TypeChecker extends visitor.NodeVisi else this.result = Integer } - else if (op === '|' || op === '^' || op === '&' || op === '%' || op === '<<' || op === '>>' || op === '>>>') { + else if (op === '%') { + this.assert((left_type === Integer || left_type === Any) && (right_type === Integer || right_type === Any), + 'invalid operands to %. They must be integer or any', node) + if (left_type === Any || right_type === Any) { + this.addCoercion(node.left, left_type) + this.addCoercion(node.right, right_type) + this.result = Any + } + else + this.result = Integer + } + else if (op === '|' || op === '^' || op === '&' || op === '<<' || op === '>>' || op === '>>>') { this.assert(left_type === Integer && right_type === Integer, this.invalidOperandsMessage(op, left_type, right_type), node) this.result = Integer @@ -722,9 +733,17 @@ export default class TypeChecker extends visitor.NodeVisi this.addCoercion(node.right, right_type) } } + else if (op === '%=') { + this.assert((left_type === Integer || left_type === Any) && (right_type === Integer || right_type === Any), + 'invalid operands to %=. They must be integer or any', node) + if (left_type === Any || right_type === Any) { + this.addCoercion(node.left, left_type) + this.addCoercion(node.right, right_type) + } + } else if (op === '|=' || op === '^=' || op === '&=' || op === '%=' || op === '<<=' || op === '>>=') this.assert(left_type === Integer && right_type === Integer, - this.invalidOperandsMessage(op, left_type, right_type), node) + this.invalidOperandsMessage(op, left_type, right_type), node) else // '||=', '&&=', '>>>=', '**=', op === '??=' this.assert(false, `not supported operator '${op}'`, node) diff --git a/server/tests/transpiler/code-generator/code-generator2.test.ts b/server/tests/transpiler/code-generator/code-generator2.test.ts index 7dea94d..479a80f 100644 --- a/server/tests/transpiler/code-generator/code-generator2.test.ts +++ b/server/tests/transpiler/code-generator/code-generator2.test.ts @@ -237,3 +237,103 @@ print(barbar(7)) const imp = new Importer(modules) expect(importAndCompileAndRun(src, imp.importer(), imp.init(), imp.files(), imp.path)).toBe('8\n') }) + +test('/ and /= operators', () => { + const src = ` + let a = 239 + let b = a / 13 + print(b) + a /= 4 + print(a) +` + + expect(compileAndRun(src)).toBe('18\n59\n') + + const src1 = ` + let a = 239.3 + let b = a / 13.2 + print(b) + a /= 4.3 + print(a) +` + + expect(compileAndRun(src1)).toBe('18.128788\n55.651165\n') + + const src2 = ` + let a: any = 239 + let b = a / 13 + print(b) + let c: any = 13 + let d = a / c + print(d) + let e = 239 / c + print(e) + let f = a /= 4 + print(a) + print(f) + print(typeof f) + let i: integer = 239 + let j = i /= c + print(i) + print(j) + print(typeof j) + ` + + expect(compileAndRun(src2)).toBe('18\n18\n18\n59\n59\nany\n18\n18\ninteger\n') + + const src3 = ` + let a: any = 239.3 + let b = a / 13.2 + print(b) + let c: any = 13.2 + let d = a / c + print(d) + let e = 239.3 / c + print(e) + let f = a /= 4.7 + print(a) + print(f) + print(typeof f) + let i: float = 239.3 + let j = i /= c + print(i) + print(j) + print(typeof j) + ` + + expect(compileAndRun(src3)).toBe('18.128788\n18.128788\n18.128788\n50.914898\n50.914898\nany\n18.128788\n18.128788\nfloat\n') +}) + +test('% and %= operators', () => { + const src = ` + let a = 239 + let b = a % 13 + print(b) + a %= 4 + print(a) +` + + expect(compileAndRun(src)).toBe('5\n3\n') + + const src2 = ` + let a: any = 239 + let b = a % 13 + print(b) + let c: any = 13 + let d = a % c + print(d) + let e = 239 % c + print(e) + let f = a %= 4 + print(a) + print(f) + print(typeof f) + let i: integer = 239 + let j = i %= c + print(i) + print(j) + print(typeof j) + ` + + expect(compileAndRun(src2)).toBe('5\n5\n5\n3\n3\nany\n5\n5\ninteger\n') +}) \ No newline at end of file