TODO: `default_value` issues.

pull/1246/head
Rick Carlino 2019-06-24 14:48:34 -05:00
parent adda53433b
commit 6b8b45bf6b
6 changed files with 97 additions and 98 deletions

View File

@ -4,8 +4,8 @@ module Api
def index
render json: sequences
.to_a
.map { |s| CeleryScript::FetchCelery.run!(sequence: s) }
.to_a
.map { |s| CeleryScript::FetchCelery.run!(sequence: s) }
end
def show
@ -18,8 +18,8 @@ module Api
def update
mutate Sequences::Update.run(sequence_params,
device: current_device,
sequence: sequence)
device: current_device,
sequence: sequence)
end
def destroy
@ -29,7 +29,7 @@ module Api
private
def sequence_params
@sequence_params ||= raw_json[:sequence] || raw_json || {}
@sequence_params ||= raw_json[:sequence] || raw_json || {}
end
def sequences

View File

@ -6,9 +6,10 @@
module CeleryScript
class AstLeaf < AstBase
FRIENDLY_ERRORS = CeleryScript::Checker::FRIENDLY_ERRORS
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
attr_reader :kind, :value, :parent
def initialize(parent, value, kind)
@parent, @value, @kind = parent, value, kind
end
@ -17,14 +18,13 @@ module CeleryScript
allowed = corpus.fetchArg(kind).allowed_values
unless allowed.any? { |spec| spec.valid?(self, corpus) }
message = (FRIENDLY_ERRORS.dig(kind, parent.kind) || BAD_LEAF) % {
kind: kind,
kind: kind,
parent_kind: parent.kind,
allowed: "[#{allowed.map(&:name).join(", ")}]",
actual: value.class
allowed: "[#{allowed.map(&:name).join(", ")}]",
actual: value.class,
}
raise TypeCheckError, message
end
end
end
end

View File

@ -3,102 +3,102 @@
# other nodes (*always* optional).
module CeleryScript
class AstNode < AstBase
attr_reader :args, :body, :comment, :kind, :parent
BODY_HAS_NON_NODES = "The `body` of a node can only contain nodes- " \
"no leaves here."
LEAVES_NEED_KEYS = "Tried to initialize a leaf without a key."
NEVER = :__NEVER__
FRIENDLY_ERRORS = CeleryScript::Checker::FRIENDLY_ERRORS
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
attr_reader :args, :body, :comment, :kind, :parent
BODY_HAS_NON_NODES = "The `body` of a node can only contain nodes- " \
"no leaves here."
LEAVES_NEED_KEYS = "Tried to initialize a leaf without a key."
NEVER = :__NEVER__
FRIENDLY_ERRORS = CeleryScript::Checker::FRIENDLY_ERRORS
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
def initialize(parent = nil, args:, body: nil, comment: "", kind:, uuid: nil)
@comment, @kind, @parent = comment, kind, parent
def initialize(parent = nil, args:, body: nil, comment: "", kind:, uuid: nil)
@comment, @kind, @parent = comment, kind, parent
@args = args.map do |key, value|
[key.to_sym, maybe_initialize(self, value, key)]
end.to_h if args
@args = args.map do |key, value|
[key.to_sym, maybe_initialize(self, value, key)]
end.to_h if args
@body = body.map do |e|
raise TypeCheckError, BODY_HAS_NON_NODES unless is_node?(e)
maybe_initialize(self, e)
end if body
@body = body.map do |e|
raise TypeCheckError, BODY_HAS_NON_NODES unless is_node?(e)
maybe_initialize(self, e)
end if body
end
def maybe_initialize(parent, leaf_or_node, key = NEVER)
if is_node?(leaf_or_node)
AstNode.new(parent, leaf_or_node)
else
raise TypeCheckError, LEAVES_NEED_KEYS if key == NEVER
AstLeaf.new(parent, leaf_or_node, key)
end
end
def is_node?(hash)
hash.is_a?(Hash) &&
hash.symbolize_keys! &&
hash.has_key?(:kind) &&
hash.has_key?(:args) &&
(hash[:body].is_a?(Array) || hash[:body] == nil) &&
(hash[:comment].is_a?(String) || hash[:comment] == nil) &&
(hash[:args].is_a?(Hash)) &&
(hash[:kind].is_a?(String))
end
# Calling this method with only one parameter
# indicates a starting condition 🏁
def resolve_variable!(origin = self)
locals = args[:locals]
if locals&.kind === "scope_declaration"
label = origin.args[:label]&.value
result = (locals.body || []).select do |x|
x.args[:label]&.value == label
end.first
return result if result
end
def maybe_initialize(parent, leaf_or_node, key = NEVER)
if is_node?(leaf_or_node)
AstNode.new(parent, leaf_or_node)
else
raise TypeCheckError, LEAVES_NEED_KEYS if key == NEVER
AstLeaf.new(parent, leaf_or_node, key)
end
case parent
when AstNode
# sequence: Check the `scope` arg
# Keep recursing if we can't find a scope on this node
parent.resolve_variable!(origin)
when nil # We've got an unbound variable.
origin.invalidate!(UNBOUND_VAR % origin.args[:label].value)
end
end
def is_node?(hash)
hash.is_a?(Hash) &&
hash.symbolize_keys! &&
hash.has_key?(:kind) &&
hash.has_key?(:args) &&
(hash[:body].is_a?(Array) || hash[:body] == nil) &&
(hash[:comment].is_a?(String) || hash[:comment] == nil) &&
(hash[:args].is_a?(Hash)) &&
(hash[:kind].is_a?(String))
end
# Calling this method with only one parameter
# indicates a starting condition 🏁
def resolve_variable!(origin = self)
locals = args[:locals]
if locals&.kind === "scope_declaration"
label = origin.args[:label]&.value
result = (locals.body || []).select do |x|
x.args[:label]&.value == label
end.first
return result if result
end
case parent
when AstNode
# sequence: Check the `scope` arg
# Keep recursing if we can't find a scope on this node
parent.resolve_variable!(origin)
when nil # We've got an unbound variable.
origin.invalidate!(UNBOUND_VAR % origin.args[:label].value)
end
end
def todo
def todo
# Don't delete this- it is currently unreachable code, but as soon as we
# allow identifiers other than `point`, `tool` and `coordinate` we will
# need it again (and can write tests)
end
end
def cross_check(corpus, key)
actual = kind
allowed = corpus.fetchArg(key).allowed_values
# It would be safe to run type checking here.
if (actual == "identifier")
allowed_types = allowed.filter { |x| x.tag == :identifier }
var = resolve_variable!
case var.kind
when "parameter_declaration" then todo
when "variable_declaration"
# REASSIGNMENT WARNING!:
actual = var.args[:data_value].kind
end
end
unless allowed.map(&:name).include?(actual)
message = (FRIENDLY_ERRORS.dig(kind, parent.kind) || BAD_LEAF) % {
kind: kind,
parent_kind: parent.kind,
allowed: allowed.map(&:name),
actual: actual
}
raise TypeCheckError, message
def cross_check(corpus, key)
actual = kind
allowed = corpus.fetchArg(key).allowed_values
# It would be safe to run type checking here.
if (actual == "identifier")
allowed_types = allowed.filter { |x| x.tag == :identifier }
var = resolve_variable!
case var.kind
when "parameter_declaration" then todo
when "variable_declaration"
# REASSIGNMENT WARNING!:
actual = var.args[:data_value].kind
end
end
unless allowed.map(&:name).include?(actual)
message = (FRIENDLY_ERRORS.dig(kind, parent.kind) || BAD_LEAF) % {
kind: kind,
parent_kind: parent.kind,
allowed: allowed.map(&:name),
actual: actual,
}
raise TypeCheckError, message
end
end
end
end

View File

@ -28,9 +28,8 @@ module CeleryScript
variable_declaration: MISSING_VAR,
parameter_declaration: MISSING_VAR,
read_pin: "You must select a Sensor in the Read Sensor step.",
move_to: "You must select a Location in the Move To step.",
execute: "You must select a Sequence in the Execute step.",
# default_value: "TODO",
move_absolute: "You must select a Location in the Move To step.",
execute: CeleryScriptSettingsBag::NO_SUB_SEQ,
},
}.with_indifferent_access

View File

@ -67,7 +67,7 @@ module CeleryScriptSettingsBag
BAD_TOOL_ID = "Tool #%s does not exist."
CANT_ANALOG = "Analog modes are not supported for Box LEDs"
NO_PIN_ID = "%s requires a valid pin number"
NO_SUB_SEQ = "missing a sequence selection for `execute` block."
NO_SUB_SEQ = "You must select a Sequence in the Execute step."
ONLY_ONE_COORD = "Move Absolute does not accept a group of locations " \
"as input. Please change your selection to a single" \
" location."

View File

@ -82,7 +82,7 @@ describe CeleryScript::Checker do
]
chk = CeleryScript::Checker.new(tree, corpus, device)
expect(chk.valid?).to be false
expect(chk.error.message).to eq("missing a sequence selection for `execute` block.")
expect(chk.error.message).to eq("You must select a Sequence in the Execute step.")
end
it "validates peripheral presence" do