From 3a9aa7b02ec9b7632bd6d85664882a6acd1ca09d Mon Sep 17 00:00:00 2001 From: chibash Date: Thu, 10 Oct 2024 01:35:46 +0900 Subject: [PATCH 1/2] supports undefined type and undefined values --- microcontroller/core/include/c-runtime.h | 2 +- .../src/transpiler/code-generator/code-generator.ts | 5 +++++ server/src/transpiler/type-checker.ts | 5 +++++ server/src/transpiler/types.ts | 2 ++ .../code-generator/code-generator.test.ts | 13 +++++++------ .../code-generator/test-code-generator.ts | 4 ++-- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/microcontroller/core/include/c-runtime.h b/microcontroller/core/include/c-runtime.h index 9e1e5bd..f6e306e 100644 --- a/microcontroller/core/include/c-runtime.h +++ b/microcontroller/core/include/c-runtime.h @@ -87,7 +87,7 @@ inline value_t ptr_to_value(pointer_t v) { return (value_t)(((uintptr_t)v & 0xff inline bool is_ptr_value(value_t v) { return (v & 3) == 3; } #define VALUE_NULL 3 // null pointer: 0000 ... 0011 -#define VALUE_UNDEF 0 +#define VALUE_UNDEF 3 // equivalent to VALUE_NULL #define VALUE_ZERO 0 // integer 0 #define VALUE_FZERO 1 // float 0.0 #define VALUE_FALSE 0 // 0000 ... 0000 (integer 0) diff --git a/server/src/transpiler/code-generator/code-generator.ts b/server/src/transpiler/code-generator/code-generator.ts index b5ffd43..20a9879 100644 --- a/server/src/transpiler/code-generator/code-generator.ts +++ b/server/src/transpiler/code-generator/code-generator.ts @@ -155,6 +155,11 @@ export class CodeGenerator extends visitor.NodeVisitor { } identifier(node: AST.Identifier, env: VariableEnv): void { + if (node.name === 'undefined') { + this.result.write('VALUE_UNDEF') + return + } + const info = env.table.lookup(node.name) if (info !== undefined) { if (info.isFunction) { diff --git a/server/src/transpiler/type-checker.ts b/server/src/transpiler/type-checker.ts index 2b15ea0..0c2a95d 100644 --- a/server/src/transpiler/type-checker.ts +++ b/server/src/transpiler/type-checker.ts @@ -165,6 +165,11 @@ export default class TypeChecker extends visitor.NodeVisi } identifier(node: AST.Identifier, names: NameTable): void { + if (node.name === 'undefined') { + this.result = Null + return + } + const nameInfo = names.lookup(node.name) if (nameInfo !== undefined) { const info = nameInfo as NameInfo diff --git a/server/src/transpiler/types.ts b/server/src/transpiler/types.ts index 5048ddf..0d2ae6c 100644 --- a/server/src/transpiler/types.ts +++ b/server/src/transpiler/types.ts @@ -119,6 +119,8 @@ export class ArrayType extends ObjectType { export function typeToString(type: StaticType): string { if (type instanceof CompositeType) return type.sourceName() + else if (type === Null) + return 'undefined' else return type } diff --git a/server/tests/transpiler/code-generator/code-generator.test.ts b/server/tests/transpiler/code-generator/code-generator.test.ts index d106792..21ff21c 100644 --- a/server/tests/transpiler/code-generator/code-generator.test.ts +++ b/server/tests/transpiler/code-generator/code-generator.test.ts @@ -89,12 +89,12 @@ test('literals', () => { } foo(33) ` - expect(compileAndRun(src)).toBe('null\n33\n7.400000\n1\n0\ntest\n') + expect(compileAndRun(src)).toBe('undefined\n33\n7.400000\n1\n0\ntest\n') }) test('undefined', () => { const src = 'const k = undefined' - expect(() => { compileAndRun(src) }).toThrow(/unknown name: undefined/) + compileAndRun(src) }) test('control structuers', () => { @@ -658,9 +658,10 @@ test('typeof operator', () => { print(typeof('foo')) print(typeof([1, 2, 3])) print(typeof(null)) + print(typeof(undefined)) print(typeof(foo)) ` - expect(compileAndRun(src)).toBe('integer\ninteger\nstring\ninteger[]\nnull\n(any) => any\n') + expect(compileAndRun(src)).toBe('integer\ninteger\nstring\ninteger[]\nundefined\nundefined\n(any) => any\n') }) test('++/-- operator', () => { @@ -828,7 +829,7 @@ test('any to null', () => { print(foo(null)) ` - expect(compileAndRun(src)).toBe('null\n') + expect(compileAndRun(src)).toBe('undefined\n') }) test('null to any or string', () => { @@ -847,7 +848,7 @@ test('null to any or string', () => { } ` - expect(compileAndRun(src)).toBe('null\n') + expect(compileAndRun(src)).toBe('undefined\n') expect(() => compileAndRun(src2)).toThrow(/not assignable.*line 3/) }) @@ -954,7 +955,7 @@ test('function call', () => { print(baz(null, [1, 2], 11, 'baz2')) ` - expect(compileAndRun(src)).toBe([7, 3, '7.400000', 'null', 1, 'baz2', 11].join('\n') + '\n') + expect(compileAndRun(src)).toBe([7, 3, '7.400000', 'undefined', 1, 'baz2', 11].join('\n') + '\n') }) test('array access', () => { diff --git a/server/tests/transpiler/code-generator/test-code-generator.ts b/server/tests/transpiler/code-generator/test-code-generator.ts index 21f18af..41836f1 100644 --- a/server/tests/transpiler/code-generator/test-code-generator.ts +++ b/server/tests/transpiler/code-generator/test-code-generator.ts @@ -23,8 +23,8 @@ static void fbody_print(value_t self, value_t m) { printf("%d\\n", value_to_int(m)); else if (is_float_value(m)) printf("%f\\n", value_to_float(m)); - else if (m == VALUE_NULL) - puts("null"); + else if (m == VALUE_NULL || m == VALUE_UNDEF) + puts("undefined"); else if (gc_is_string_literal(m)) puts(gc_string_literal_cstr(m)); else From 849b2b24cb63123e7058bacf431e9b8fe10d8763 Mon Sep 17 00:00:00 2001 From: chibash Date: Thu, 10 Oct 2024 19:23:02 +0900 Subject: [PATCH 2/2] changes the type system so that boolean will be not a subtype of any other types --- .../transpiler/code-generator/c-runtime.ts | 4 ++- server/src/transpiler/types.ts | 2 +- .../code-generator/code-generator.test.ts | 27 +++++++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/server/src/transpiler/code-generator/c-runtime.ts b/server/src/transpiler/code-generator/c-runtime.ts index 197eea3..61d82a6 100644 --- a/server/src/transpiler/code-generator/c-runtime.ts +++ b/server/src/transpiler/code-generator/c-runtime.ts @@ -111,8 +111,10 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un else break case BooleanT: - if (from === Integer || from === Float) + if (from === Integer) return '(' + else if (from === Float) + return '(int32_t)(' else if (from === Any) return 'safe_value_to_bool(' else diff --git a/server/src/transpiler/types.ts b/server/src/transpiler/types.ts index 0d2ae6c..c02895e 100644 --- a/server/src/transpiler/types.ts +++ b/server/src/transpiler/types.ts @@ -163,7 +163,7 @@ export function isSubtype(subtype: StaticType, type: StaticType): boolean { || subtype === StringT && type === objectType) return true else if (type === BooleanT) - return subtype !== Void && subtype !== Any + return subtype === BooleanT else if (subtype instanceof CompositeType) return subtype.isSubtypeOf(type) else diff --git a/server/tests/transpiler/code-generator/code-generator.test.ts b/server/tests/transpiler/code-generator/code-generator.test.ts index 21ff21c..5376590 100644 --- a/server/tests/transpiler/code-generator/code-generator.test.ts +++ b/server/tests/transpiler/code-generator/code-generator.test.ts @@ -36,7 +36,7 @@ test('boolean conditions', () => { let c: any = 3 while (c) if (c-- < 0) - b = null + b = true else if (c < -10) { let str: any = null let k = str + 1 @@ -70,10 +70,33 @@ test('boolean conditions', () => { expect(compileAndRun(src)).toEqual('') }) +test('wrong assignment to boolean', () => { + const src = ` + let b = true + b = null + ` + + expect(() => { compileAndRun(src) }).toThrow(/assignable to type 'boolean'/) + + const src2 = ` + let b = true + b = 3 + ` + + expect(() => { compileAndRun(src2) }).toThrow(/assignable to type 'boolean'/) + + const src3 = ` + let b = true + b = 'foo' + ` + + expect(() => { compileAndRun(src2) }).toThrow(/assignable to type 'boolean'/) +}) + test('literals', () => { const src = ` function foo(n: integer) { - const empty = null + const empty = null // or undefined const i = n const f = 7.4 const b1 = true