-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #75 from project-eutopia/freezing_of_variables
Freezing of variables
- Loading branch information
Showing
10 changed files
with
248 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
module Keisan | ||
module AST | ||
class CellAssignment | ||
attr_reader :assignment, :context, :lhs, :rhs | ||
|
||
def initialize(assignment, context, lhs, rhs) | ||
@assignment = assignment | ||
@context = context | ||
@lhs = lhs | ||
@rhs = rhs | ||
end | ||
|
||
def evaluate | ||
lhs = lhs_evaluate_and_check_modifiable | ||
|
||
unless lhs.is_a?(Cell) | ||
raise Exceptions::InvalidExpression.new("Unhandled left hand side #{lhs} in assignment") | ||
end | ||
|
||
case assignment.compound_operator | ||
when :"||" | ||
evaluate_cell_or_assignment(context, lhs, rhs) | ||
when :"&&" | ||
evaluate_cell_and_assignment(context, lhs, rhs) | ||
else | ||
evaluate_cell_non_logical_assignment(context, lhs, rhs) | ||
end | ||
end | ||
|
||
private | ||
|
||
def lhs_evaluate_and_check_modifiable | ||
lhs.evaluate(context) | ||
rescue RuntimeError => e | ||
raise Exceptions::UnmodifiableError.new("Cannot modify frozen variables") if e.message =~ /can't modify frozen/ | ||
raise | ||
end | ||
|
||
def evaluate_cell_or_assignment(context, lhs, rhs) | ||
if lhs.false? | ||
rhs = rhs.evaluate(context) | ||
lhs.node = rhs.is_a?(Cell) ? rhs.node.deep_dup : rhs | ||
rhs | ||
else | ||
lhs | ||
end | ||
end | ||
|
||
def evaluate_cell_and_assignment(context, lhs, rhs) | ||
if lhs.true? | ||
rhs = rhs.evaluate(context) | ||
lhs.node = rhs.is_a?(Cell) ? rhs.node.deep_dup : rhs | ||
rhs | ||
else | ||
lhs | ||
end | ||
end | ||
|
||
def evaluate_cell_non_logical_assignment(context, lhs, rhs) | ||
rhs = rhs.evaluate(context) | ||
if assignment.compound_operator | ||
rhs = rhs.send(assignment.compound_operator, lhs.node).evaluate(context) | ||
end | ||
|
||
lhs.node = rhs.is_a?(Cell) ? rhs.node.deep_dup : rhs | ||
rhs | ||
end | ||
end | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
module Keisan | ||
module AST | ||
class FunctionAssignment | ||
attr_reader :context, :lhs, :rhs, :local | ||
|
||
def initialize(context, lhs, rhs, local) | ||
@context = context | ||
@lhs = lhs | ||
@rhs = rhs | ||
@local = local | ||
|
||
unless lhs.children.all? {|arg| arg.is_a?(Variable)} | ||
raise Exceptions::InvalidExpression.new("Left hand side function must have variables as arguments") | ||
end | ||
end | ||
|
||
def argument_names | ||
@argument_names ||= lhs.children.map(&:name) | ||
end | ||
|
||
def expression_function | ||
Functions::ExpressionFunction.new( | ||
lhs.name, | ||
argument_names, | ||
rhs.evaluate_assignments(context.spawn_child(shadowed: argument_names, transient: true)), | ||
context.transient_definitions | ||
) | ||
end | ||
|
||
def evaluate | ||
# Blocks might have local variable/function definitions, so skip check | ||
verify_rhs_of_function_assignment_is_valid! unless rhs.is_a?(Block) | ||
|
||
context.register_function!(lhs.name, expression_function, local: local) | ||
rhs | ||
end | ||
|
||
private | ||
|
||
def verify_rhs_of_function_assignment_is_valid! | ||
# Only variables that can appear are those that are arguments to the function | ||
unless rhs.unbound_variables(context) <= Set.new(argument_names) | ||
raise Exceptions::InvalidExpression.new("Unbound variables found in function definition") | ||
end | ||
# Cannot have undefined functions unless allowed by context | ||
unless context.allow_recursive || rhs.unbound_functions(context).empty? | ||
raise Exceptions::InvalidExpression.new("Unbound function definitions are not allowed by current context") | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
module Keisan | ||
module AST | ||
class VariableAssignment | ||
attr_reader :assignment, :context, :lhs, :rhs | ||
|
||
def initialize(assignment, context, lhs, rhs) | ||
@assignment = assignment | ||
@context = context | ||
@lhs = lhs | ||
@rhs = rhs | ||
end | ||
|
||
def evaluate | ||
case assignment.compound_operator | ||
when :"||" | ||
evaluate_variable_or_assignment(context, lhs, rhs) | ||
when :"&&" | ||
evaluate_variable_and_assignment(context, lhs, rhs) | ||
else | ||
evaluate_variable_non_logical_assignment(context, lhs, rhs) | ||
end | ||
end | ||
|
||
private | ||
|
||
def evaluate_variable_or_assignment(context, lhs, rhs) | ||
if lhs.variable_truthy?(context) | ||
lhs | ||
else | ||
rhs = rhs.evaluate(context) | ||
context.register_variable!(lhs.name, rhs.value(context)) | ||
rhs | ||
end | ||
end | ||
|
||
def evaluate_variable_and_assignment(context, lhs, rhs) | ||
if lhs.variable_truthy?(context) | ||
rhs = rhs.evaluate(context) | ||
context.register_variable!(lhs.name, rhs.value(context)) | ||
rhs | ||
else | ||
context.register_variable!(lhs.name, nil) unless context.has_variable?(lhs.name) | ||
lhs | ||
end | ||
end | ||
|
||
def evaluate_variable_non_logical_assignment(context, lhs, rhs) | ||
rhs = rhs.evaluate(context) | ||
rhs_value = rhs.value(context) | ||
|
||
if assignment.compound_operator | ||
raise Exceptions::InvalidExpression.new("Compound assignment requires variable #{lhs.name} to already exist") unless context.has_variable?(lhs.name) | ||
rhs_value = context.variable(lhs.name).value.send(assignment.compound_operator, rhs_value) | ||
end | ||
|
||
context.register_variable!(lhs.name, rhs_value, local: assignment.local) | ||
# Return the variable assigned value | ||
rhs | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.