1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-28 00:59:31 +00:00

Change equality test to reference comparison for mutable values

This commit is contained in:
Étienne Fildadut 2022-09-09 14:54:45 +09:00
parent b50d783928
commit bac5cdde01
8 changed files with 315 additions and 87 deletions

View file

@ -682,6 +682,39 @@ These can be used to represent some caracters in string and other text elements
Only `0` and `nil` are false. Everything else is considered true. Only `0` and `nil` are false. Everything else is considered true.
#### Equality
Anselme consider two objects to be equal if they can *always* be used interchangeably.
In practice, this results in two main cases for equality tests:
* immutable values (strings, numbers, constants, ...). They are compared by recursively making sure all of their values and structure are equal.
```
(All of the following if true)
~ 5 == 5
~ "foo" == "foo"
~ constant([1,2,3]) == constant([1,2,3])
```
* mutable values (list, map, objects that are not constant). They are compared by reference, i.e. they are only considered equal if they are not distinct objects, even if they contain the same values and structure.
```
:a = [1,2,3]
:b = a
:c = [1,2,3]
(True:)
~ a == b
(False:)
~ a == c
(a and c are not interchangeable as they are two distinct lists; if we do:)
~ a(1) = 42
(This will change the first value of both a and b, but not c.)
```
#### Refering to an identifier #### Refering to an identifier
Any defined identifier can be accessed from an expression by using its name; the identifier will be first searched in the current namespace, then go up until it finds it as described in [identifiers](#identifiers). Any defined identifier can be accessed from an expression by using its name; the identifier will be first searched in the current namespace, then go up until it finds it as described in [identifiers](#identifiers).
@ -930,9 +963,9 @@ Built-in operators:
##### Comparaison ##### Comparaison
`a == b`: returns `1` if a and b have the same value (will recursively compare list and pairs), `0` otherwise `a == b`: returns `1` if a and b are considered equal, `0` otherwise
`a != b`: returns `1` if a and b do not have the same value, `0` otherwise `a != b`: returns `1` if a and b are not equal, `0` otherwise
These only work on numbers: These only work on numbers:

View file

@ -150,10 +150,17 @@ common = {
get_variable = function(state, fqm) get_variable = function(state, fqm)
local var = state.variables[fqm] local var = state.variables[fqm]
if var.type == "pending definition" then if var.type == "pending definition" then
-- evaluate
local v, e = eval(state, var.value.expression) local v, e = eval(state, var.value.expression)
if not v then if not v then
return nil, ("%s; while evaluating default value for variable %q defined at %s"):format(e, fqm, var.value.source) return nil, ("%s; while evaluating default value for variable %q defined at %s"):format(e, fqm, var.value.source)
end end
-- make constant if variable is constant
if state.variable_constants[fqm] then
v = copy(v)
common.mark_constant(v)
end
-- set variable
local s, err = common.set_variable(state, fqm, v, state.variable_constants[fqm]) local s, err = common.set_variable(state, fqm, v, state.variable_constants[fqm])
if not s then return nil, err end if not s then return nil, err end
return v return v
@ -164,13 +171,10 @@ common = {
--- set the value of a variable --- set the value of a variable
-- returns true -- returns true
-- returns nil, err -- returns nil, err
set_variable = function(state, name, val, defining_a_constant) set_variable = function(state, name, val, bypass_constant_check)
if val.type ~= "pending definition" then if val.type ~= "pending definition" then
-- check constant -- check constant
if defining_a_constant then if not bypass_constant_check then
val = copy(val)
common.mark_constant(val)
else
local s, e = common.check_mutable(state, name) local s, e = common.check_mutable(state, name)
if not s then if not s then
return nil, ("%s; while assigning value to variable %q"):format(e, name) return nil, ("%s; while assigning value to variable %q"):format(e, name)
@ -270,23 +274,15 @@ common = {
return true return true
end end
end, end,
--- compare two anselme value for equality --- compare two anselme values for equality.
-- for immutable values or constants: compare by value
-- for mutable values: compare by reference
compare = function(a, b) compare = function(a, b)
if a.type ~= b.type then if a.type ~= b.type or a.constant ~= b.constant then
return false return false
end end
if a.type == "pair" or a.type == "annotated" then if a.type == "pair" or a.type == "annotated" then
return common.compare(a.value[1], b.value[1]) and common.compare(a.value[2], b.value[2]) return common.compare(a.value[1], b.value[1]) and common.compare(a.value[2], b.value[2])
elseif a.type == "list" then
if #a.value ~= #b.value then
return false
end
for i, v in ipairs(a.value) do
if not common.compare(v, b.value[i]) then
return false
end
end
return true
elseif a.type == "function reference" then elseif a.type == "function reference" then
if #a.value ~= #b.value then if #a.value ~= #b.value then
return false return false
@ -304,6 +300,39 @@ common = {
end end
end end
return true return true
-- mutable types: need to be constant
elseif a.constant and a.type == "list" then
if #a.value ~= #b.value then
return false
end
for i, v in ipairs(a.value) do
if not common.compare(v, b.value[i]) then
return false
end
end
return true
elseif a.constant and a.type == "object" then
if a.value.class ~= b.value.class then
return false
end
-- check every attribute redefined in a and b
-- NOTE: comparaison will fail if an attribute has been redefined in only one of the object, even if it was set to the same value as the original class attribute
local compared = {}
for name, v in pairs(a.value.attributes) do
compared[name] = true
if not b.value.attributes[name] or not common.compare(v, b.value.attributes[name]) then
return false
end
end
for name, v in pairs(b.value.attributes) do
if not compared[name] then
if not a.value.attributes[name] or not common.compare(v, a.value.attributes[name]) then
return false
end
end
end
return true
-- the rest
else else
return a.value == b.value return a.value == b.value
end end

View file

@ -1,4 +1,4 @@
local format, to_lua, from_lua, events, anselme local format, to_lua, from_lua, events, anselme, escape
local types = {} local types = {}
types.lua = { types.lua = {
@ -99,8 +99,7 @@ types.anselme = {
end, end,
to_lua = function(val) to_lua = function(val)
local l = {} local l = {}
-- handle non-pair before pairs as LuaJIT's table.insert -- handle non-pair before pairs as LuaJIT's table.insert will always insert after the last element even if there are some nil before unlike PUC
-- will always insert after the last element even if there are some nil before unlike PUC
for _, v in ipairs(val) do for _, v in ipairs(val) do
if v.type ~= "pair" then if v.type ~= "pair" then
local s, e = to_lua(v) local s, e = to_lua(v)
@ -168,7 +167,15 @@ types.anselme = {
}, },
object = { object = {
format = function(val) format = function(val)
return ("%%%s"):format(val.class) local attributes = {}
for name, v in pairs(val.attributes) do
table.insert(attributes, ("%s=%s"):format(name:gsub("^"..escape(val.class)..".", ""), format(v)))
end
if #attributes > 0 then
return ("%%%s(%s)"):format(val.class, table.concat(attributes, ", "))
else
return ("%%%s"):format(val.class)
end
end, end,
to_lua = nil to_lua = nil
}, },
@ -186,5 +193,6 @@ package.loaded[...] = types
local common = require((...):gsub("stdlib%.types$", "interpreter.common")) local common = require((...):gsub("stdlib%.types$", "interpreter.common"))
format, to_lua, from_lua, events = common.format, common.to_lua, common.from_lua, common.events format, to_lua, from_lua, events = common.format, common.to_lua, common.from_lua, common.events
anselme = require((...):gsub("stdlib%.types$", "anselme")) anselme = require((...):gsub("stdlib%.types$", "anselme"))
escape = require((...):gsub("stdlib%.types$", "parser.common")).escape
return types return types

View file

@ -1,19 +1,29 @@
:a = [1=2] ::a = [1=2]
0 = {a == [5=2]} 0 = {a == [5=2]!constant}
0 = {a == [1=3]} 0 = {a == [1=3]!constant}
1 = {a == [1=2]} 1 = {a == [1=2]!constant}
:b = [1,2,3] ::b = [1,2,3]
0 = {b == a} 0 = {b == a}
0 = {b == []} 0 = {b == []!constant}
0 = {b == [3,1,2]} 0 = {b == [3,1,2]!constant}
0 = {b == [1,2,3,4]} 0 = {b == [1,2,3,4]!constant}
1 = {b == [1,2,3]} 1 = {b == [1,2,3]!constant}
:c = [1,2,3]
0 = {c == b}
1 = {c!constant == b}
::d = [1,2,3]
1 = {d == b}

View file

@ -1,101 +1,148 @@
local _={} local _={}
_[41]={} _[67]={}
_[40]={} _[66]={}
_[39]={} _[65]={}
_[38]={} _[64]={}
_[37]={} _[63]={}
_[36]={} _[62]={}
_[35]={} _[61]={}
_[34]={} _[60]={}
_[33]={tags=_[41],text="1"} _[59]={}
_[32]={tags=_[41],text="1 = "} _[58]={}
_[31]={tags=_[40],text="0"} _[57]={}
_[30]={tags=_[40],text="0 = "} _[56]={}
_[29]={tags=_[39],text="0"} _[55]={}
_[28]={tags=_[39],text="0 = "} _[54]={}
_[27]={tags=_[38],text="0"} _[53]={}
_[26]={tags=_[38],text="0 = "} _[52]={}
_[25]={tags=_[37],text="0"} _[51]={}
_[24]={tags=_[37],text="0 = "} _[50]={}
_[23]={tags=_[36],text="1"} _[49]={}
_[22]={tags=_[36],text="1 = "} _[48]={}
_[21]={tags=_[35],text="0"} _[47]={}
_[20]={tags=_[35],text="0 = "} _[46]={}
_[19]={tags=_[34],text="0"} _[45]={tags=_[67],text="1"}
_[18]={tags=_[34],text="0 = "} _[44]={tags=_[66],text="1 = "}
_[43]={tags=_[65],text="1"}
_[42]={tags=_[64],text="1 = "}
_[41]={tags=_[63],text="0"}
_[40]={tags=_[62],text="0 = "}
_[39]={tags=_[61],text="1"}
_[38]={tags=_[60],text="1 = "}
_[37]={tags=_[59],text="0"}
_[36]={tags=_[58],text="0 = "}
_[35]={tags=_[57],text="0"}
_[34]={tags=_[56],text="0 = "}
_[33]={tags=_[55],text="0"}
_[32]={tags=_[54],text="0 = "}
_[31]={tags=_[53],text="0"}
_[30]={tags=_[52],text="0 = "}
_[29]={tags=_[51],text="1"}
_[28]={tags=_[50],text="1 = "}
_[27]={tags=_[49],text="0"}
_[26]={tags=_[48],text="0 = "}
_[25]={tags=_[47],text="0"}
_[24]={tags=_[46],text="0 = "}
_[23]={_[44],_[45]}
_[22]={_[42],_[43]}
_[21]={_[40],_[41]}
_[20]={_[38],_[39]}
_[19]={_[36],_[37]}
_[18]={_[34],_[35]}
_[17]={_[32],_[33]} _[17]={_[32],_[33]}
_[16]={_[30],_[31]} _[16]={_[30],_[31]}
_[15]={_[28],_[29]} _[15]={_[28],_[29]}
_[14]={_[26],_[27]} _[14]={_[26],_[27]}
_[13]={_[24],_[25]} _[13]={_[24],_[25]}
_[12]={_[22],_[23]} _[12]={"return"}
_[11]={_[20],_[21]} _[11]={"text",_[23]}
_[10]={_[18],_[19]} _[10]={"text",_[22]}
_[9]={"return"} _[9]={"text",_[21]}
_[8]={"text",_[17]} _[8]={"text",_[20]}
_[7]={"text",_[16]} _[7]={"text",_[19]}
_[6]={"text",_[15]} _[6]={"text",_[18]}
_[5]={"text",_[14]} _[5]={"text",_[17]}
_[4]={"text",_[13]} _[4]={"text",_[16]}
_[3]={"text",_[12]} _[3]={"text",_[15]}
_[2]={"text",_[11]} _[2]={"text",_[14]}
_[1]={"text",_[10]} _[1]={"text",_[13]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9]} return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12]}
--[[ --[[
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "0 = " text = "0 = "
}, { }, {
tags = <table 1>, tags = {},
text = "0" text = "0"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "0 = " text = "0 = "
}, { }, {
tags = <table 1>, tags = {},
text = "0" text = "0"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "1 = " text = "1 = "
}, { }, {
tags = <table 1>, tags = {},
text = "1" text = "1"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "0 = " text = "0 = "
}, { }, {
tags = <table 1>, tags = {},
text = "0" text = "0"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "0 = " text = "0 = "
}, { }, {
tags = <table 1>, tags = {},
text = "0" text = "0"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "0 = " text = "0 = "
}, { }, {
tags = <table 1>, tags = {},
text = "0" text = "0"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "0 = " text = "0 = "
}, { }, {
tags = <table 1>, tags = {},
text = "0" text = "0"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "1 = " text = "1 = "
}, { }, {
tags = <table 1>, tags = {},
text = "1"
} } }
{ "text", { {
tags = {},
text = "0 = "
}, {
tags = {},
text = "0"
} } }
{ "text", { {
tags = {},
text = "1 = "
}, {
tags = {},
text = "1"
} } }
{ "text", { {
tags = {},
text = "1 = "
}, {
tags = {},
text = "1" text = "1"
} } } } } }
{ "return" } { "return" }

View file

@ -0,0 +1,23 @@
% class
:a:b = "foo"
:c = "bar"
:o = class
:a = class
::b = class
0 = {o == a}
0 = {o == b}
1 = {o!constant == b}
~ o.b := "haha"
0 = {o!constant == b}
~ a.b := "haha"
1 = {o!constant == a!constant}
~ o.b := "foo"
0 = {o!constant == b}

View file

@ -0,0 +1,78 @@
local _={}
_[33]={}
_[32]={}
_[31]={}
_[30]={}
_[29]={}
_[28]={}
_[27]={}
_[26]={}
_[25]={}
_[24]={}
_[23]={}
_[22]={}
_[21]={tags=_[33],text="0"}
_[20]={tags=_[32],text="0 = "}
_[19]={tags=_[31],text="1"}
_[18]={tags=_[30],text="1 = "}
_[17]={tags=_[29],text="0"}
_[16]={tags=_[28],text="0 = "}
_[15]={tags=_[27],text="1"}
_[14]={tags=_[26],text="1 = "}
_[13]={tags=_[25],text="0"}
_[12]={tags=_[24],text="0 = "}
_[11]={tags=_[23],text="0"}
_[10]={tags=_[22],text="0 = "}
_[9]={_[20],_[21]}
_[8]={_[18],_[19]}
_[7]={_[16],_[17]}
_[6]={_[10],_[11],_[12],_[13],_[14],_[15]}
_[5]={"return"}
_[4]={"text",_[9]}
_[3]={"text",_[8]}
_[2]={"text",_[7]}
_[1]={"text",_[6]}
return {_[1],_[2],_[3],_[4],_[5]}
--[[
{ "text", { {
tags = {},
text = "0 = "
}, {
tags = {},
text = "0"
}, {
tags = {},
text = "0 = "
}, {
tags = {},
text = "0"
}, {
tags = {},
text = "1 = "
}, {
tags = {},
text = "1"
} } }
{ "text", { {
tags = {},
text = "0 = "
}, {
tags = {},
text = "0"
} } }
{ "text", { {
tags = {},
text = "1 = "
}, {
tags = {},
text = "1"
} } }
{ "text", { {
tags = {},
text = "0 = "
}, {
tags = {},
text = "0"
} } }
{ "return" }
]]--

View file

@ -14,7 +14,7 @@ _[12]={tags=_[21],text="hoho"}
_[11]={tags=_[20],text="bar"} _[11]={tags=_[20],text="bar"}
_[10]={tags=_[19],text=" == "} _[10]={tags=_[19],text=" == "}
_[9]={tags=_[18],text="bar"} _[9]={tags=_[18],text="bar"}
_[8]={tags=_[17],text="%object constructor.class::&object constructor.class"} _[8]={tags=_[17],text="%object constructor.class(c=hoho)::&object constructor.class"}
_[7]={tags=_[16],text=", "} _[7]={tags=_[16],text=", "}
_[6]={tags=_[15],text="%object constructor.class::&object constructor.class"} _[6]={tags=_[15],text="%object constructor.class::&object constructor.class"}
_[5]={_[9],_[10],_[11],_[12],_[13],_[14]} _[5]={_[9],_[10],_[11],_[12],_[13],_[14]}
@ -32,7 +32,7 @@ return {_[1],_[2],_[3]}
text = ", " text = ", "
}, { }, {
tags = {}, tags = {},
text = "%object constructor.class::&object constructor.class" text = "%object constructor.class(c=hoho)::&object constructor.class"
} } } } } }
{ "text", { { { "text", { {
tags = {}, tags = {},