-
-
Notifications
You must be signed in to change notification settings - Fork 673
Implement resolveBinaryExpression #689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ import { | |
Field, | ||
FieldPrototype, | ||
Global, | ||
TypeDefinition | ||
TypeDefinition, TypedElement | ||
} from "./program"; | ||
|
||
import { | ||
|
@@ -1188,6 +1188,30 @@ export class Resolver extends DiagnosticEmitter { | |
return null; | ||
} | ||
|
||
private binaryOperatorKindFromToken(token: Token): OperatorKind { | ||
switch (token) { | ||
case Token.PLUS: return OperatorKind.ADD; | ||
case Token.MINUS: return OperatorKind.MINUS; | ||
case Token.ASTERISK: return OperatorKind.MUL; | ||
case Token.SLASH: return OperatorKind.DIV; | ||
case Token.PERCENT: return OperatorKind.REM; | ||
case Token.ASTERISK_ASTERISK: return OperatorKind.POW; | ||
case Token.AMPERSAND: return OperatorKind.BITWISE_AND; | ||
case Token.BAR: return OperatorKind.BITWISE_OR; | ||
case Token.CARET: return OperatorKind.BITWISE_XOR; | ||
case Token.LESSTHAN_LESSTHAN: return OperatorKind.BITWISE_SHL; | ||
case Token.GREATERTHAN_GREATERTHAN: return OperatorKind.BITWISE_SHR; | ||
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: return OperatorKind.BITWISE_SHR_U; | ||
case Token.EQUALS_EQUALS: return OperatorKind.EQ; | ||
case Token.EXCLAMATION_EQUALS: return OperatorKind.NE; | ||
case Token.GREATERTHAN: return OperatorKind.GT; | ||
case Token.GREATERTHAN_EQUALS: return OperatorKind.GE; | ||
case Token.LESSTHAN: return OperatorKind.LT; | ||
case Token.LESSTHAN_EQUALS: return OperatorKind.LE; | ||
default: return OperatorKind.INVALID; | ||
} | ||
} | ||
|
||
/** Resolves a binary expression to the program element it refers to. */ | ||
resolveBinaryExpression( | ||
/** The expression to resolve. */ | ||
|
@@ -1199,14 +1223,42 @@ export class Resolver extends DiagnosticEmitter { | |
/** How to proceed with eventualy diagnostics. */ | ||
reportMode: ReportMode = ReportMode.REPORT | ||
): Element | null { | ||
// TODO | ||
if (reportMode == ReportMode.REPORT) { | ||
this.error( | ||
DiagnosticCode.Operation_not_supported, | ||
expression.range | ||
); | ||
let left = expression.left; | ||
let right = expression.right; | ||
let leftElement = this.resolveExpression(left, flow, contextualType, reportMode); | ||
if (!leftElement) return null; | ||
let leftType = (<TypedElement>leftElement!).type; | ||
let rightElement = this.resolveExpression(right, flow, leftType, reportMode); | ||
if (!rightElement) return null; | ||
let rightType = (<TypedElement>rightElement!).type; | ||
let commonType: Type | null; | ||
if (commonType = Type.commonDenominator(leftType, rightType, true)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some occasions where it doesn't work this way, like for shifts which always prefer the LHS. I think there should be a switch over all possible operators here that mimics what |
||
let typeClasses = this.program.typeClasses; | ||
if (typeClasses.has(commonType.kind)) { | ||
return typeClasses.get(commonType.kind); | ||
} else if (commonType.classReference) { | ||
let overload = commonType.classReference.lookupOverload(this.binaryOperatorKindFromToken(expression.operator)); | ||
if (!overload) { | ||
if (reportMode == ReportMode.REPORT) { | ||
this.error( | ||
DiagnosticCode.Operation_not_supported, | ||
expression.range | ||
); | ||
} | ||
return null; | ||
} | ||
return commonType.classReference; | ||
} | ||
return null; | ||
} else { | ||
if (reportMode == ReportMode.REPORT) { | ||
this.error( | ||
DiagnosticCode.Operation_not_supported, | ||
expression.range | ||
); | ||
} | ||
return null; | ||
} | ||
return null; | ||
} | ||
|
||
/** Resolves a this expression to the program element it refers to. */ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"asc_flags": [ | ||
"--runtime none" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
(module | ||
(type $FUNCSIG$i (func (result i32))) | ||
(type $FUNCSIG$ii (func (param i32) (result i32))) | ||
(type $FUNCSIG$iii (func (param i32 i32) (result i32))) | ||
(type $FUNCSIG$viii (func (param i32 i32 i32))) | ||
(type $FUNCSIG$iij (func (param i32 i64) (result i32))) | ||
(type $FUNCSIG$v (func)) | ||
(type $FUNCSIG$ij (func (param i64) (result i32))) | ||
(memory $0 1) | ||
(data (i32.const 8) "\02\00\00\00\01\00\00\00\01\00\00\00\02\00\00\000") | ||
(global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) | ||
(global $~lib/rt/stub/offset (mut i32) (i32.const 0)) | ||
(export "memory" (memory $0)) | ||
(export "operatorAccessLiteral" (func $resolve-operator/operatorAccessLiteral)) | ||
(export "operatorAccess" (func $resolve-operator/operatorAccess)) | ||
(export "OperatorOverloadingAccess" (func $resolve-operator/OperatorOverloadingAccess)) | ||
(export "OperatorOverloadingSubclassAccess" (func $resolve-operator/OperatorOverloadingSubclassAccess)) | ||
(start $start) | ||
(func $~lib/util/number/decimalCount32 (; 0 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) | ||
i32.const 1 | ||
i32.const 2 | ||
local.get $0 | ||
i32.const 10 | ||
i32.lt_u | ||
select | ||
i32.const 3 | ||
i32.const 4 | ||
i32.const 5 | ||
local.get $0 | ||
i32.const 10000 | ||
i32.lt_u | ||
select | ||
local.get $0 | ||
i32.const 1000 | ||
i32.lt_u | ||
select | ||
local.get $0 | ||
i32.const 100 | ||
i32.lt_u | ||
select | ||
i32.const 6 | ||
i32.const 7 | ||
local.get $0 | ||
i32.const 1000000 | ||
i32.lt_u | ||
select | ||
i32.const 8 | ||
i32.const 9 | ||
i32.const 10 | ||
local.get $0 | ||
i32.const 1000000000 | ||
i32.lt_u | ||
select | ||
local.get $0 | ||
i32.const 100000000 | ||
i32.lt_u | ||
select | ||
local.get $0 | ||
i32.const 10000000 | ||
i32.lt_u | ||
select | ||
local.get $0 | ||
i32.const 100000 | ||
i32.lt_u | ||
select | ||
) | ||
(func $~lib/rt/stub/__alloc (; 1 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) | ||
(local $2 i32) | ||
(local $3 i32) | ||
(local $4 i32) | ||
(local $5 i32) | ||
local.get $0 | ||
i32.const 1073741808 | ||
i32.gt_u | ||
if | ||
unreachable | ||
end | ||
global.get $~lib/rt/stub/offset | ||
i32.const 16 | ||
i32.add | ||
local.tee $3 | ||
local.get $0 | ||
i32.const 1 | ||
local.get $0 | ||
i32.const 1 | ||
i32.gt_u | ||
select | ||
i32.add | ||
i32.const 15 | ||
i32.add | ||
i32.const -16 | ||
i32.and | ||
local.tee $2 | ||
memory.size | ||
local.tee $4 | ||
i32.const 16 | ||
i32.shl | ||
i32.gt_u | ||
if | ||
local.get $4 | ||
local.get $2 | ||
local.get $3 | ||
i32.sub | ||
i32.const 65535 | ||
i32.add | ||
i32.const -65536 | ||
i32.and | ||
i32.const 16 | ||
i32.shr_u | ||
local.tee $5 | ||
local.get $4 | ||
local.get $5 | ||
i32.gt_s | ||
select | ||
memory.grow | ||
i32.const 0 | ||
i32.lt_s | ||
if | ||
local.get $5 | ||
memory.grow | ||
i32.const 0 | ||
i32.lt_s | ||
if | ||
unreachable | ||
end | ||
end | ||
end | ||
local.get $2 | ||
global.set $~lib/rt/stub/offset | ||
local.get $3 | ||
i32.const 16 | ||
i32.sub | ||
local.tee $2 | ||
local.get $1 | ||
i32.store offset=8 | ||
local.get $2 | ||
local.get $0 | ||
i32.store offset=12 | ||
local.get $3 | ||
) | ||
(func $~lib/util/number/utoa_simple<u32> (; 2 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) | ||
(local $3 i32) | ||
loop $continue|0 | ||
local.get $1 | ||
i32.const 10 | ||
i32.rem_u | ||
local.set $3 | ||
local.get $1 | ||
i32.const 10 | ||
i32.div_u | ||
local.set $1 | ||
local.get $2 | ||
i32.const 1 | ||
i32.sub | ||
local.tee $2 | ||
i32.const 1 | ||
i32.shl | ||
local.get $0 | ||
i32.add | ||
local.get $3 | ||
i32.const 48 | ||
i32.add | ||
i32.store16 | ||
local.get $1 | ||
br_if $continue|0 | ||
end | ||
) | ||
(func $~lib/util/number/utoa32 (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) | ||
(local $1 i32) | ||
(local $2 i32) | ||
local.get $0 | ||
i32.eqz | ||
if | ||
i32.const 24 | ||
return | ||
end | ||
local.get $0 | ||
call $~lib/util/number/decimalCount32 | ||
local.tee $1 | ||
i32.const 1 | ||
i32.shl | ||
i32.const 1 | ||
call $~lib/rt/stub/__alloc | ||
local.tee $2 | ||
local.get $0 | ||
local.get $1 | ||
call $~lib/util/number/utoa_simple<u32> | ||
local.get $2 | ||
) | ||
(func $resolve-operator/operatorAccessLiteral (; 4 ;) (type $FUNCSIG$i) (result i32) | ||
i32.const 3 | ||
call $~lib/util/number/utoa32 | ||
) | ||
(func $resolve-operator/operatorAccess (; 5 ;) (type $FUNCSIG$i) (result i32) | ||
(local $0 i32) | ||
(local $1 i32) | ||
i32.const 3 | ||
call $~lib/util/number/decimalCount32 | ||
local.tee $0 | ||
i32.const 1 | ||
i32.shl | ||
i32.const 1 | ||
call $~lib/rt/stub/__alloc | ||
local.tee $1 | ||
i32.const 3 | ||
local.get $0 | ||
call $~lib/util/number/utoa_simple<u32> | ||
local.get $1 | ||
) | ||
(func $resolve-operator/Container#constructor (; 6 ;) (type $FUNCSIG$iij) (param $0 i32) (param $1 i64) (result i32) | ||
local.get $0 | ||
i32.eqz | ||
if | ||
i32.const 8 | ||
i32.const 3 | ||
call $~lib/rt/stub/__alloc | ||
local.set $0 | ||
end | ||
local.get $0 | ||
i64.const 0 | ||
i64.store | ||
local.get $0 | ||
local.get $1 | ||
i64.store | ||
local.get $0 | ||
) | ||
(func $resolve-operator/Container.add (; 7 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) | ||
i32.const 0 | ||
local.get $0 | ||
i64.load | ||
local.get $1 | ||
i64.load | ||
i64.add | ||
call $resolve-operator/Container#constructor | ||
) | ||
(func $resolve-operator/OperatorOverloadingAccess (; 8 ;) (type $FUNCSIG$i) (result i32) | ||
i32.const 0 | ||
i64.const 1 | ||
call $resolve-operator/Container#constructor | ||
i32.const 0 | ||
i64.const 2 | ||
call $resolve-operator/Container#constructor | ||
call $resolve-operator/Container.add | ||
call $~lib/util/number/utoa32 | ||
) | ||
(func $resolve-operator/SubContainer#constructor (; 9 ;) (type $FUNCSIG$ij) (param $0 i64) (result i32) | ||
i32.const 8 | ||
i32.const 4 | ||
call $~lib/rt/stub/__alloc | ||
local.get $0 | ||
call $resolve-operator/Container#constructor | ||
) | ||
(func $resolve-operator/OperatorOverloadingSubclassAccess (; 10 ;) (type $FUNCSIG$i) (result i32) | ||
i64.const 1 | ||
call $resolve-operator/SubContainer#constructor | ||
i64.const 2 | ||
call $resolve-operator/SubContainer#constructor | ||
call $resolve-operator/Container.add | ||
call $~lib/util/number/utoa32 | ||
) | ||
(func $start (; 11 ;) (type $FUNCSIG$v) | ||
i32.const 32 | ||
global.set $~lib/rt/stub/startOffset | ||
global.get $~lib/rt/stub/startOffset | ||
global.set $~lib/rt/stub/offset | ||
) | ||
(func $null (; 12 ;) (type $FUNCSIG$v) | ||
nop | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's always a
TypedElement
, I thinkresolveExpression
should have it as the return type. Unless it's not always one, ofc, which would mean it misses something here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I thought, but what's an example of something that does not resolve to a
typedElement
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What can happen there for example is that if an expression referencing a class name is resolved, the element is a static
ClassPrototype
without a type. The same should be true for function names, because the function can be generic with multiple instances so aFunctionPrototype
is returned. There might be others that I forgot.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Is it possible to circumvent this by using
resolveType
here instead ofresolveExpression
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolveType
is meant for type nodes exclusively here, so I think it isn't applicable. If I'm not mistaken the resolver should actually return theClassPrototype
respectivelyFunctionProtoype
here (since we want the element), and whatever the caller ofresolveExpression
is would deal with it appropriately.