diff --git a/alicorn-expressions.lua b/alicorn-expressions.lua index 7d58a681..4f435bb3 100644 --- a/alicorn-expressions.lua +++ b/alicorn-expressions.lua @@ -123,6 +123,11 @@ local collect_host_tuple ---@field env Environment local ExpressionArgs = {} +---@class TopLevelBlockArgs +---@field exprargs ExpressionArgs +---@field name string +local TopLevelBlockArgs = {} + ---Unpack ExpressionArgs into component parts ---@return expression_goal ---@return Environment @@ -230,8 +235,9 @@ end ---@param yard { n: integer, [integer]: TaggedOperator } ---@param output { n: integer, [integer]: ConstructedSyntax } ----@param anchor Anchor -local function shunting_yard_pop(yard, output, anchor) +---@param start_anchor Anchor +---@param end_anchor Anchor +local function shunting_yard_pop(yard, output, start_anchor, end_anchor) local yard_height = yard.n local output_length = output.n local operator = yard[yard_height] @@ -239,14 +245,25 @@ local function shunting_yard_pop(yard, output, anchor) local operator_symbol = operator.symbol if operator_type == OperatorType.Prefix then local arg = output[output_length] - local tree = metalanguage.list(anchor, metalanguage.symbol(anchor, operator_symbol), arg) + local tree = metalanguage.list( + start_anchor, + end_anchor, + metalanguage.symbol(start_anchor, end_anchor, operator_symbol), + arg + ) yard[yard_height] = nil yard.n = yard_height - 1 output[output_length] = tree elseif operator_type == OperatorType.Infix then local right = output[output_length] local left = output[output_length - 1] - local tree = metalanguage.list(anchor, left, metalanguage.symbol(anchor, operator_symbol), right) + local tree = metalanguage.list( + start_anchor, + end_anchor, + left, + metalanguage.symbol(start_anchor, end_anchor, operator_symbol), + right + ) yard[yard_height] = nil yard.n = yard_height - 1 output[output_length] = nil @@ -301,10 +318,11 @@ end ---@param b ConstructedSyntax ---@param yard { n: integer, [integer]: TaggedOperator } ---@param output { n: integer, [integer]: ConstructedSyntax } ----@param anchor Anchor +---@param start_anchor Anchor +---@param end_anchor Anchor ---@return boolean ---@return ConstructedSyntax|string -local function shunting_yard(a, b, yard, output, anchor) +local function shunting_yard(a, b, yard, output, start_anchor, end_anchor) -- first, collect all prefix operators local is_prefix, prefix_symbol = a:match({ metalanguage.issymbol(shunting_yard_prefix_handler) }, metalanguage.failure_handler, nil) @@ -322,7 +340,7 @@ local function shunting_yard(a, b, yard, output, anchor) type = OperatorType.Prefix, symbol = prefix_symbol, } - return shunting_yard(next_a, next_b, yard, output, anchor) + return shunting_yard(next_a, next_b, yard, output, start_anchor, end_anchor) end -- no more prefix operators, now handle infix output.n = output.n + 1 @@ -340,19 +358,19 @@ local function shunting_yard(a, b, yard, output, anchor) end if not more then while yard.n > 0 do - shunting_yard_pop(yard, output, anchor) + shunting_yard_pop(yard, output, start_anchor, end_anchor) end return true, output[1] end while yard.n > 0 and shunting_yard_should_pop(infix_symbol, yard[yard.n]) do - shunting_yard_pop(yard, output, anchor) + shunting_yard_pop(yard, output, start_anchor, end_anchor) end yard.n = yard.n + 1 yard[yard.n] = { type = OperatorType.Infix, symbol = infix_symbol, } - return shunting_yard(next_a, next_b, yard, output, anchor) + return shunting_yard(next_a, next_b, yard, output, start_anchor, end_anchor) end ---@param symbol string @@ -470,7 +488,7 @@ local function expression_pairhandler(args, a, b) -- if the expression is a list containing prefix and infix expressions, -- parse it into a tree of simple prefix/infix expressions with shunting yard - local ok, syntax = shunting_yard(a, b, { n = 0 }, { n = 0 }, a.anchor) + local ok, syntax = shunting_yard(a, b, { n = 0 }, { n = 0 }, a.start_anchor, a.end_anchor) if ok then ---@cast syntax ConstructedSyntax is_operator, operator_type, operator, left, right = syntax:match({ @@ -494,13 +512,13 @@ local function expression_pairhandler(args, a, b) if not ok then return false, combiner end - sargs = metalanguage.list(a.anchor, left) + sargs = metalanguage.list(a.start_anchor, a.end_anchor, left) elseif is_operator and operator_type == OperatorType.Infix then ok, combiner = env:get("_" .. operator .. "_") if not ok then return false, combiner end - sargs = metalanguage.list(a.anchor, left, right) + sargs = metalanguage.list(a.start_anchor, a.end_anchor, left, right) else ok, combiner, env = a:match( { expression(metalanguage.accept_handler, ExpressionArgs.new(expression_goal.infer, env)) }, @@ -557,11 +575,11 @@ local function expression_pairhandler(args, a, b) -- if not operative_result_val:is_enum_value() then -- p(operative_result_val.kind) -- print(operative_result_val:pretty_print()) - -- return false, "applying operative did not result in value term with kind enum_value, typechecker or lua operative mistake when applying " .. tostring(a.anchor) .. " to the args " .. tostring(b.anchor) + -- return false, "applying operative did not result in value term with kind enum_value, typechecker or lua operative mistake when applying " .. tostring(a.start_anchor) .. " to the args " .. tostring(b.start_anchor) -- end -- variants: ok, error --if operative_result_val.variant == "error" then - -- return false, semantic_error.operative_apply_failed(operative_result_val.data, { a.anchor, b.anchor }) + -- return false, semantic_error.operative_apply_failed(operative_result_val.data, { a.start_anchor, b.start_anchor }) --end -- temporary, while it isn't a Maybe @@ -638,7 +656,7 @@ local function expression_pairhandler(args, a, b) ---@cast res inferrable if result_info:unwrap_result_info():unwrap_result_info():is_effectful() then - local bind = terms.binding.program_sequence(res, a.anchor) + local bind = terms.binding.program_sequence(res, a.start_anchor) env = env:bind_local(bind) ok, res = env:get("#program-sequence") --TODO refactor if not ok then @@ -668,7 +686,7 @@ local function expression_pairhandler(args, a, b) }, metalanguage.failure_handler, nil) if not ok then - error(semantic_error.host_function_argument_collect_failed(tuple, { a.anchor, b.anchor }, { + error(semantic_error.host_function_argument_collect_failed(tuple, { a.start_anchor, b.start_anchor }, { host_function_type = type_of_term, host_function_value = term, }, orig_env)) @@ -693,7 +711,7 @@ local function expression_pairhandler(args, a, b) ) ) ---@type Environment - env = env:bind_local(terms.binding.program_sequence(app, a.anchor)) + env = env:bind_local(terms.binding.program_sequence(app, a.start_anchor)) ok, res = env:get("#program-sequence") if not ok then error(res) @@ -849,7 +867,7 @@ expression = metalanguage.reducer( ---@class OperativeError ---@field cause any ----@field anchor Anchor +---@field start_anchor Anchor ---@field operative_name string local OperativeError = {} local external_error_mt = { @@ -857,7 +875,7 @@ local external_error_mt = { local message = "Lua error occured inside host operative " .. self.operative_name .. " " - .. (self.anchor and tostring(self.anchor) or " at unknown position") + .. (self.start_anchor and tostring(self.start_anchor) or " at unknown position") .. ":\n" .. tostring(self.cause) return message @@ -866,12 +884,14 @@ local external_error_mt = { } ---@param cause any ----@param anchor Anchor +---@param start_anchor Anchor +---@param end_anchor Anchor ---@param operative_name any ---@return OperativeError -function OperativeError.new(cause, anchor, operative_name) +function OperativeError.new(cause, start_anchor, end_anchor, operative_name) return setmetatable({ - anchor = anchor, + start_anchor = start_anchor, + end_anchor = end_anchor, cause = cause, operative_name = operative_name, }, external_error_mt) @@ -894,7 +914,7 @@ local function host_operative(fn, name) -- userdata isn't passed in as it's always empty for host operatives local ok, res, env = fn(syn, env, goal) if not ok then - error(OperativeError.new(res, syn.anchor, debugstring)) + error(OperativeError.new(res, syn.start_anchor, syn.end_anchor, debugstring)) end if (goal:is_infer() and inferrable_term.value_check(res)) @@ -1212,6 +1232,87 @@ local block = metalanguage.reducer( "block" ) +local top_level_block = metalanguage.reducer( + ---@param syntax ConstructedSyntax + ---@param args TopLevelBlockArgs + ---@return boolean + ---@return inferrable|checkable|string + ---@return Environment? + function(syntax, args) + local goal, env = args.exprargs:unwrap() + assert(goal:is_infer(), "NYI non-infer cases for block") + local lastval = inferrable_term.tuple_cons(inferrable_array()) + local newval + local ok, continue = true, true + + --slightly hacky length measurement, but oh well + local length, tail = 0, syntax + while ok and continue do + ok, continue, tail = tail:match({ + metalanguage.ispair(function(ud, a, b) + return true, true, b + end), + metalanguage.isnil(function(ud) + return true, false + end), + }, metalanguage.failure_handler, nil) + if not ok then + return false, continue + end + length = length + 1 + end + continue = true + io.write( + "\nprocessing " + .. tostring(args.name) + .. " --- 0 / " + .. tostring(length) + .. " @ " + .. tostring(tail and tail.start_anchor or (syntax and syntax.start_anchor) or "") + .. " … " + .. tostring(tail and tail.end_anchor or (syntax and syntax.end_anchor) or "") + .. "\n" + ) + local progress = 0 + while ok and continue do + ok, continue, newval, syntax, env = syntax:match({ + metalanguage.ispair(collect_tuple_pair_handler), + metalanguage.isnil(collect_tuple_nil_handler), + }, metalanguage.failure_handler, ExpressionArgs.new(goal, env)) + if ok and continue then + lastval = newval + end + -- print("newval", tostring(newval)) + progress = progress + 1 + local line_setup_sequence = "" + if U.file_is_terminal() then + line_setup_sequence = "\x1bM" + end + io.write( + line_setup_sequence + .. "processing " + .. tostring(args.name) + .. " --- " + .. tostring(progress) + .. " / " + .. tostring(length) + .. " @ " + .. tostring(newval and newval.start_anchor or (syntax and syntax.start_anchor) or "") --FIXME wrong anchors + .. " … " + .. tostring(newval and newval.end_anchor or (syntax and syntax.end_anchor) or "") + .. "\n" + ) + end + if not ok then + io.write("\nFailed!\n") + return false, continue + end + io.write("\nFinished!\n") + return true, lastval, env + end, + "block" +) + -- example usage of primitive_applicative -- add(a, b) = a + b -> -- local prim_num = terms.value.prim_number_type @@ -1278,6 +1379,7 @@ local alicorn_expressions = { inferred_expression = inferred_expression, -- constexpr = constexpr block = block, + top_level_block = top_level_block, ExpressionArgs = ExpressionArgs, host_operative = host_operative, host_applicative = host_applicative, diff --git a/alicorn-utils.lua b/alicorn-utils.lua index dca84dd3..fee29923 100644 --- a/alicorn-utils.lua +++ b/alicorn-utils.lua @@ -264,7 +264,7 @@ end ---@return string ---@return integer function M.strip_ansi(s) - return s:gsub("\x1b[^m]*m", "") + return s:gsub("\x1b%[[^m]*m", "") end function M.here() @@ -272,4 +272,35 @@ function M.here() return " @ " .. info.source .. ":" .. info.currentline end +function M.file_is_terminal(input_file) + -- TODO + return false +end + +function M.get_cursor_position(input_file, output_file) + if input_file == nil then + input_file = io.input() + end + if output_file == nil then + output_file = io.output() + end + output_file:write("\x1b[6n") + local terminal_data = input_file:read(1) + if terminal_data ~= "\x9b" then + terminal_data = terminal_data .. input_file:read(1) + assert(terminal_data == "\x1b[") + end + terminal_data = input_file:read("*n") + assert(terminal_data ~= nil) + local cursor_line = terminal_data + terminal_data = input_file:read(1) + assert(terminal_data == ";") + terminal_data = input_file:read("*n") + assert(terminal_data ~= nil) + local cursor_column = terminal_data + terminal_data = input_file:read(1) + assert(terminal_data == "R") + return cursor_line, cursor_column +end + return M diff --git a/base-env.lua b/base-env.lua index 08d0e542..a11bb033 100644 --- a/base-env.lua +++ b/base-env.lua @@ -258,7 +258,7 @@ local function intrinsic_impl(syntax, env) error "env nil in base-env.intrinsic" end return true, - terms.inferrable_term.host_intrinsic(str, type--[[terms.checkable_term.inferrable(type)]], syntax.anchor), + terms.inferrable_term.host_intrinsic(str, type--[[terms.checkable_term.inferrable(type)]], syntax.start_anchor), env end @@ -340,7 +340,13 @@ local ascribed_name = metalanguage.reducer( local shadowed shadowed, env = env:enter_block(terms.block_purity.pure) env = env:bind_local( - terms.binding.annotated_lambda("#prev", prev, syntax.anchor, terms.visibility.explicit, literal_purity_pure) + terms.binding.annotated_lambda( + "#prev", + prev, + syntax.start_anchor, + terms.visibility.explicit, + literal_purity_pure + ) ) local ok, prev_binding = env:get("#prev") if not ok then @@ -381,7 +387,7 @@ local curry_segment = metalanguage.reducer( terms.binding.annotated_lambda( name, type_val, - syntax.anchor, + syntax.start_anchor, terms.visibility.implicit, literal_purity_pure ) @@ -757,13 +763,13 @@ local function make_host_func_syntax(effectful) local shadowed shadowed, env = env:enter_block(terms.block_purity.pure) - -- tail.anchor can be nil so we fall back to the anchor for the start of this host func type if needed + -- tail.start_anchor can be nil so we fall back to the start_anchor for this host func type if needed -- TODO: use correct name in lambda parameter instead of adding an extra let env = env:bind_local( terms.binding.annotated_lambda( "#host-func-arguments", params_args, - tail.anchor or syntax.anchor, + tail.start_anchor or syntax.start_anchor, terms.visibility.explicit, literal_purity_pure ) @@ -852,13 +858,13 @@ local function forall_type_impl(syntax, env) local shadowed shadowed, env = env:enter_block(terms.block_purity.pure) - -- tail.anchor can be nil so we fall back to the anchor for the start of this forall type if needed + -- tail.start_anchor can be nil so we fall back to the start_anchor for this forall type if needed -- TODO: use correct name in lambda parameter instead of adding an extra let env = env:bind_local( terms.binding.annotated_lambda( "#forall-arguments", params_args, - tail.anchor or syntax.anchor, + tail.start_anchor or syntax.start_anchor, terms.visibility.explicit, literal_purity_pure ) @@ -1051,7 +1057,7 @@ local function lambda_impl(syntax, env) terms.binding.annotated_lambda( "#lambda-arguments", args, - syntax.anchor, + syntax.start_anchor, terms.visibility.explicit, literal_purity_pure ) @@ -1086,7 +1092,7 @@ local function lambda_prog_impl(syntax, env) terms.binding.annotated_lambda( "#lambda-arguments", args, - syntax.anchor, + syntax.start_anchor, terms.visibility.explicit, literal_purity_effectful ) @@ -1118,7 +1124,7 @@ local function lambda_single_impl(syntax, env) local shadow, inner_env = env:enter_block(terms.block_purity.pure) inner_env = inner_env:bind_local( - terms.binding.annotated_lambda(name, arg, syntax.anchor, terms.visibility.explicit, literal_purity_pure) + terms.binding.annotated_lambda(name, arg, syntax.start_anchor, terms.visibility.explicit, literal_purity_pure) ) local ok, expr, env = tail:match( { exprs.block(metalanguage.accept_handler, exprs.ExpressionArgs.new(terms.expression_goal.infer, inner_env)) }, @@ -1145,7 +1151,7 @@ local function lambda_implicit_impl(syntax, env) local shadow, inner_env = env:enter_block(terms.block_purity.pure) inner_env = inner_env:bind_local( - terms.binding.annotated_lambda(name, arg, syntax.anchor, terms.visibility.implicit, literal_purity_pure) + terms.binding.annotated_lambda(name, arg, syntax.start_anchor, terms.visibility.implicit, literal_purity_pure) ) local ok, expr, env = tail:match( { exprs.block(metalanguage.accept_handler, exprs.ExpressionArgs.new(terms.expression_goal.infer, inner_env)) }, diff --git a/environment.lua b/environment.lua index 14621046..0027587e 100644 --- a/environment.lua +++ b/environment.lua @@ -237,10 +237,10 @@ function environment:bind_local(binding) error(res2) error("tuple elim speculation failed! debugging this is left as an exercise to the maintainer") elseif binding:is_annotated_lambda() then - local param_name, param_annotation, anchor, visible = binding:unwrap_annotated_lambda() - if not anchor or not anchor.sourceid then + local param_name, param_annotation, start_anchor, visible = binding:unwrap_annotated_lambda() + if not start_anchor or not start_anchor.sourceid then print("binding", binding) - error "missing anchor for annotated lambda binding" + error "missing start_anchor for annotated lambda binding" end local annotation_type, annotation_usages, annotation_term = infer(param_annotation, self.typechecking_context) --print("binding lambda annotation: (typed term follows)") @@ -248,7 +248,7 @@ function environment:bind_local(binding) local evaled = evaluator.evaluate(annotation_term, self.typechecking_context.runtime_context) local bindings = self.bindings:append(binding) local locals = self.locals:put(param_name, inferrable_term.bound_variable(self.typechecking_context:len() + 1)) - local typechecking_context = self.typechecking_context:append(param_name, evaled, nil, anchor) + local typechecking_context = self.typechecking_context:append(param_name, evaled, nil, start_anchor) return update_env(self, { locals = locals, bindings = bindings, @@ -259,16 +259,18 @@ function environment:bind_local(binding) if self.purity:is_pure() then error("binding.program_sequence is only allowed in effectful blocks") end - local first, anchor = binding:unwrap_program_sequence() + local first, start_anchor = binding:unwrap_program_sequence() local first_type, first_usages, first_term = infer(first, self.typechecking_context) if not first_type:is_program_type() then error("program sequence must infer to a program type") end local first_effect_sig, first_base_type = first_type:unwrap_program_type() + --print("FOUND EFFECTFUL BINDING", first_base_type, "produced by ", first_type) local n = self.typechecking_context:len() local term = inferrable_term.bound_variable(n + 1) local locals = self.locals:put("#program-sequence", term) - local typechecking_context = self.typechecking_context:append("#program-sequence", first_base_type, nil, anchor) + local typechecking_context = + self.typechecking_context:append("#program-sequence", first_base_type, nil, start_anchor) local bindings = self.bindings:append(binding) return update_env(self, { locals = locals, @@ -390,11 +392,11 @@ function environment:exit_block(term, shadowed) local names, subject = binding:unwrap_tuple_elim() wrapped = terms.inferrable_term.tuple_elim(names, subject, wrapped) elseif binding:is_annotated_lambda() then - local name, annotation, anchor, visible, purity = binding:unwrap_annotated_lambda() - wrapped = terms.inferrable_term.annotated_lambda(name, annotation, wrapped, anchor, visible, purity) + local name, annotation, start_anchor, visible, purity = binding:unwrap_annotated_lambda() + wrapped = terms.inferrable_term.annotated_lambda(name, annotation, wrapped, start_anchor, visible, purity) elseif binding:is_program_sequence() then - local first, anchor = binding:unwrap_program_sequence() - wrapped = terms.inferrable_term.program_sequence(first, anchor, wrapped) + local first, start_anchor = binding:unwrap_program_sequence() + wrapped = terms.inferrable_term.program_sequence(first, start_anchor, wrapped) else error("exit_block: unknown kind: " .. binding.kind) end diff --git a/evaluator.lua b/evaluator.lua index 7790bf62..319487fc 100644 --- a/evaluator.lua +++ b/evaluator.lua @@ -1657,11 +1657,11 @@ function infer( elseif inferrable_term:is_typed() then return inferrable_term:unwrap_typed() elseif inferrable_term:is_annotated_lambda() then - local param_name, param_annotation, body, anchor, param_visibility, purity = + local param_name, param_annotation, body, start_anchor, param_visibility, purity = inferrable_term:unwrap_annotated_lambda() local _, _, param_term = infer(param_annotation, typechecking_context) local param_type = evaluate(param_term, typechecking_context:get_runtime_context()) - local inner_context = typechecking_context:append(param_name, param_type, nil, anchor) + local inner_context = typechecking_context:append(param_name, param_type, nil, start_anchor) local _, purity_term = check(purity, inner_context, terms.host_purity_type) local body_type, body_usages, body_term = infer(body, inner_context) @@ -2173,7 +2173,7 @@ function infer( add_arrays(result_usages, bodyusages) return bodytype, result_usages, terms.typed_term.let(name, exprterm, bodyterm) elseif inferrable_term:is_host_intrinsic() then - local source, type, anchor = inferrable_term:unwrap_host_intrinsic() + local source, type, start_anchor = inferrable_term:unwrap_host_intrinsic() local source_usages, source_term = check(source, typechecking_context, value.host_string_type) local type_type, type_usages, type_term = infer(type, typechecking_context) --check(type, typechecking_context, value.qtype_type(0)) @@ -2184,7 +2184,7 @@ function infer( --error "weird type" -- FIXME: type_type, source_type are ignored, need checked? local type_val = evaluate(type_term, typechecking_context.runtime_context) - return type_val, source_usages, typed_term.host_intrinsic(source_term, anchor) + return type_val, source_usages, typed_term.host_intrinsic(source_term, start_anchor) elseif inferrable_term:is_level_max() then local level_a, level_b = inferrable_term:unwrap_level_max() local arg_type_a, arg_usages_a, arg_term_a = infer(level_a, typechecking_context) @@ -2218,13 +2218,13 @@ function infer( ) return terms.value.star(0, 0), desc_usages, terms.typed_term.host_tuple_type(desc_term) elseif inferrable_term:is_program_sequence() then - local first, anchor, continue = inferrable_term:unwrap_program_sequence() + local first, start_anchor, continue = inferrable_term:unwrap_program_sequence() local first_type, first_usages, first_term = infer(first, typechecking_context) if not first_type:is_program_type() then error("program sequence must infer to a program type") end local first_effect_sig, first_base_type = first_type:unwrap_program_type() - local inner_context = typechecking_context:append("#program-sequence", first_base_type, nil, anchor) + local inner_context = typechecking_context:append("#program-sequence", first_base_type, nil, start_anchor) local continue_type, continue_usages, continue_term = infer(continue, inner_context) if not continue_type:is_program_type() then error( @@ -2675,7 +2675,7 @@ function evaluate(typed_term, runtime_context) local expr_value = evaluate(expr, runtime_context) return evaluate(body, runtime_context:append(expr_value)) elseif typed_term:is_host_intrinsic() then - local source, anchor = typed_term:unwrap_host_intrinsic() + local source, start_anchor = typed_term:unwrap_host_intrinsic() local source_val = evaluate(source, runtime_context) if source_val:is_host_value() then local source_str = source_val:unwrap_host_value() @@ -2691,14 +2691,14 @@ function evaluate(typed_term, runtime_context) end local has_luvit_require, require_generator = pcall(require, "require") if has_luvit_require then - load_env.require = require_generator(anchor.sourceid) + load_env.require = require_generator(start_anchor.sourceid) end - local res = assert(load(source_str, "host_intrinsic<" .. tostring(anchor) .. ">", "t", load_env))() + local res = assert(load(source_str, "host_intrinsic<" .. tostring(start_anchor) .. ">", "t", load_env))() intrinsic_memo[source_str] = res return value.host_value(res) elseif source_val:is_neutral() then local source_neutral = source_val:unwrap_neutral() - return value.neutral(neutral_value.host_intrinsic_stuck(source_neutral, anchor)) + return value.neutral(neutral_value.host_intrinsic_stuck(source_neutral, start_anchor)) else error "Tried to load an intrinsic with something that isn't a string" end diff --git a/format-adapter.lua b/format-adapter.lua index c31c3085..23430346 100644 --- a/format-adapter.lua +++ b/format-adapter.lua @@ -7,17 +7,17 @@ local function syntax_convert(tree) for i = #tree.elements, 1, -1 do local elem = syntax_convert(tree.elements[i]) if elem then -- special handling for comments... - res = metalanguage.pair(tree.anchor, elem, res) + res = metalanguage.pair(tree.start_anchor, tree.end_anchor, elem, res) end end return res elseif tree.kind == "symbol" then - return metalanguage.symbol(tree.anchor, tree.str) + return metalanguage.symbol(tree.start_anchor, tree.end_anchor, tree.str) elseif tree.kind == "literal" then - return metalanguage.value(tree.anchor, { type = tree.literaltype, val = tree.val }) + return metalanguage.value(tree.start_anchor, tree.end_anchor, { type = tree.literaltype, val = tree.val }) elseif tree.kind == "string" then if type(tree.elements) == "string" then - return metalanguage.value(tree.anchor, { type = "string", val = tree.elements }) + return metalanguage.value(tree.start_anchor, tree.end_anchor, { type = "string", val = tree.elements }) end if #tree.elements ~= 1 or tree.elements[1].literaltype ~= "bytes" then error "NYI: strings with splices / not exactly one literal" @@ -29,7 +29,7 @@ local function syntax_convert(tree) chars[i] = string.char(byte) end local val = table.concat(chars) - return metalanguage.value(tree.anchor, { type = "string", val = val }) + return metalanguage.value(tree.start_anchor, tree.end_anchor, { type = "string", val = val }) elseif tree.kind == "comment" then --do nothing else @@ -48,6 +48,26 @@ local function lispy_print(code, d) if d == nil then d = 0 end + local start_anchor_pfx = "" + if code.start_anchor ~= nil then + local start_anchor_pfx_components = {} + table.insert(start_anchor_pfx_components, code.start_anchor.sourceid) + table.insert(start_anchor_pfx_components, code.start_anchor.line) + table.insert(start_anchor_pfx_components, code.start_anchor.char) + if #start_anchor_pfx_components > 0 then + start_anchor_pfx = "#|" .. table.concat(start_anchor_pfx_components, ":") .. "…|# " + end + end + local end_anchor_sfx = "" + if code.end_anchor ~= nil then + local end_anchor_sfx_components = {} + table.insert(end_anchor_sfx_components, code.end_anchor.sourceid) + table.insert(end_anchor_sfx_components, code.end_anchor.line) + table.insert(end_anchor_sfx_components, code.end_anchor.char) + if #end_anchor_sfx_components > 0 then + end_anchor_sfx = " #|…" .. table.concat(end_anchor_sfx_components, ":") .. "|#" + end + end if code.accepters.Pair then local hd = code[1] local tl = code[2] @@ -73,24 +93,30 @@ local function lispy_print(code, d) end local pfx1 = pfx .. lispy_indent if t >= lispy_break then - return ("(\n%s%s\n%s)"):format(pfx1, table.concat(a, "\n" .. pfx1), pfx) + return ("%s(\n%s%s\n%s)%s"):format( + start_anchor_pfx, + pfx1, + table.concat(a, "\n" .. pfx1), + pfx, + end_anchor_sfx + ) else - return ("(%s)"):format(table.concat(a, " ")) + return ("%s(%s)%s"):format(start_anchor_pfx, table.concat(a, " "), end_anchor_sfx) end elseif code.accepters.Symbol then local name = code[1] - return name + return start_anchor_pfx .. name .. end_anchor_sfx elseif code.accepters.Value then local val = code[1] local sval = string.gsub(tostring(val.val), "%c", "") if #sval > 10 then sval = string.sub(sval, 1, 10) .. "..." end - return ("val[%s](%s)"):format(val.type, sval) + return ("%sval[%s](%s)%s"):format(start_anchor_pfx, val.type, sval, end_anchor_sfx) elseif code.accepters.Nil then - return "()" + return start_anchor_pfx .. "()" .. end_anchor_sfx else - error("awa") + error(start_anchor_pfx .. "awa" .. end_anchor_sfx) end end diff --git a/format.lua b/format.lua index 449aaf8a..b734d408 100644 --- a/format.lua +++ b/format.lua @@ -7,6 +7,14 @@ local P, C, Cg, Cc, Cmt, Ct, Cb, Cp, Cf, Cs, S, V, R = -- documentation for the SLN: https://scopes.readthedocs.io/en/latest/dataformat/ -- a python SLN parser: https://github.com/salotz/python-sln/blob/master/src/sln/parser.py +local function DebugPrint(s, patt) + patt = P(function() + print(s) + return true + end) * patt + return patt +end + ---@class Anchor ---@field line integer ---@field char integer @@ -24,7 +32,12 @@ local anchor_mt = { return (snd.line == fst.line and snd.char == fst.char) end, __tostring = function(self) - return "in file " .. self.sourceid .. ", line " .. self.line .. " character " .. self.char + return "file " + .. tostring(self.sourceid) + .. ", line " + .. tostring(self.line) + .. " character " + .. tostring(self.char) end, __index = Anchor, } @@ -32,11 +45,11 @@ local anchor_mt = { lpeg.locale(lpeg) local function element(kind, pattern) - return Ct(Cg(V "textpos", "anchor") * Cg(Cc(kind), "kind") * pattern) + return Ct(Cg(V "anchor", "start_anchor") * Cg(Cc(kind), "kind") * pattern) end local function symbol(value) - return element("symbol", Cg(value, "str")) + return element("symbol", Cg(value, "str") * Cg(V "anchor", "end_anchor")) end local function space_tokens(pattern) @@ -85,19 +98,19 @@ local function IFRmt(pattern, numtimes) end local function list(pattern) - return (V "textpos" * Ct(pattern) * V "textpos") - / function(anchor, elements, endpos) + return (V "anchor" * Ct(pattern) * V "anchor") + / function(start_anchor, elements, end_anchor) return { - anchor = anchor, + start_anchor = start_anchor, elements = elements, - endpos = endpos, + end_anchor = end_anchor, kind = "list", } end end ---@class Literal ----@field anchor Anchor +---@field start_anchor Anchor ---@field kind LiteralKind ---@field literaltype LiteralType? ---@field val number | table | nil @@ -114,23 +127,23 @@ local function update_ffp(name, patt) return patt + ( - Cmt(lpeg.Carg(2) * V "textpos", function(_, _, ctx, position) - if ctx.position then - if ctx.position == position then + Cmt(lpeg.Carg(2) * V "anchor", function(_, _, furthest_forward_ctx, start_anchor) + if furthest_forward_ctx.start_anchor then + if furthest_forward_ctx.start_anchor == start_anchor then local acc = true - for i, v in ipairs(ctx.expected) do + for i, v in ipairs(furthest_forward_ctx.expected) do acc = acc and not (v == name) end if acc then - table.insert(ctx.expected, name) + table.insert(furthest_forward_ctx.expected, name) end - elseif ctx.position < position then - ctx.position = position - ctx.expected = { name } + elseif furthest_forward_ctx.start_anchor < start_anchor then + furthest_forward_ctx.start_anchor = start_anchor + furthest_forward_ctx.expected = { name } end else - ctx.position = position - ctx.expected = { name } + furthest_forward_ctx.start_anchor = start_anchor + furthest_forward_ctx.expected = { name } end return false @@ -139,16 +152,17 @@ local function update_ffp(name, patt) end local function clear_ffp() - return lpeg.Carg(2) / function(ctx) - ctx.position = nil - ctx.expected = nil - end + return lpeg.Carg(2) + / function(furthest_forward_ctx) + furthest_forward_ctx.start_anchor = nil + furthest_forward_ctx.expected = nil + end end -local function create_literal(anchor, elements, endpos) +local function create_literal(start_anchor, elements, end_anchor) local val = { - anchor = anchor, - endpos = endpos, + start_anchor = start_anchor, + end_anchor = end_anchor, kind = "literal", literaltype = "bytes", val = {}, @@ -170,13 +184,29 @@ end ---@param sourceid string ---@return Anchor local function create_anchor(line, char, sourceid) - local newanchor = { + local new_anchor = { line = line, char = char, sourceid = sourceid, } - setmetatable(newanchor, anchor_mt) - return newanchor + setmetatable(new_anchor, anchor_mt) + return new_anchor +end + +---@class LinePosition +---@field line integer +---@field pos integer +local LinePosition = {} + +local line_position_mt = { + __tostring = function(self) + return "line " .. tostring(self.line) .. " starting at position " .. tostring(self.pos) + end, + __index = LinePosition, +} + +local function create_line_position(pos, line) + return setmetatable({ pos = pos, line = line }, line_position_mt) end local grammar = P { @@ -194,36 +224,35 @@ local grammar = P { end), eof = P(-1), - newline = (P "\r" ^ 0 * P "\n") * Cmt(lpeg.Carg(1), function(_, position, table) - if not (table.positions[#table.positions].pos == position) then - if table.positions[#table.positions].pos < position then - table.positions[#table.positions + 1] = - { pos = position, line = table.positions[#table.positions].line + 1 } - end + newline = (P "\r" ^ 0 * P "\n") * Cmt(lpeg.Carg(1), function(_, position, line_ctx) + if line_ctx.positions[#line_ctx.positions].pos < position then + -- print("new line! last line_ctx position:", tostring(line_ctx.positions[#line_ctx.positions])) + line_ctx.positions[#line_ctx.positions + 1] = + create_line_position(position, line_ctx.positions[#line_ctx.positions].line + 1) end return true end), empty_line = V "newline" * S "\t " ^ 0 * #(V "newline" + V "eof"), - textpos = Cmt(lpeg.Carg(1), function(_, position, linectx) - -- assert(position > table.positions[#table.positions].pos) - local line_index = #linectx.positions + anchor = Cmt(lpeg.Carg(1), function(_, position, line_ctx) + local line_index = #line_ctx.positions + -- assert(line_ctx.positions[line_index].pos <= position, "assertion failed! anchor at " .. tostring(position) .. " means backtracking to before " .. tostring(line_ctx.positions[line_index])) - while (position < linectx.positions[line_index].pos) and (line_index > 0) do + while (position < line_ctx.positions[line_index].pos) and (0 < line_index) do line_index = line_index - 1 end - local simple = create_anchor( - linectx.positions[line_index].line, - position - linectx.positions[line_index].pos + 1, - linectx.sourceid + local simple_anchor = create_anchor( + line_ctx.positions[line_index].line, + position - line_ctx.positions[line_index].pos + 1, + line_ctx.sourceid ) - return true, simple + return true, simple_anchor end), count_tabs = update_ffp( "spaces should not be interspersed in indentation", - Cmt(V "textpos" * C(S "\t " ^ 0), function(_, _, anchor, indentstring) + Cmt(V "anchor" * C(S "\t " ^ 0), function(_, _, start_anchor, indentstring) if string.find(indentstring, " ") then return false end @@ -278,33 +307,33 @@ local grammar = P { escape_chars = Cs(P [[\\]] / [[\]] + P [[\"]] / [["]] + P [[\n]] / "\n" + P [[\r]] / "\r" + P [[\t]] / "\t"), unicode_escape = P "\\u" * (V "hex_digit") ^ -4, - string_literal = V "textpos" * Cs( + string_literal = V "anchor" * Cs( (V "escape_chars" + V "unicode_escape" + C(1 - (S [["\]] + V "newline" + V "splice"))) ^ 1 - ) * V "textpos" / create_literal, + ) * V "anchor" / create_literal, string = element( "string", P '"' * Cg(Ct((V "string_literal" + V "splice") ^ 0), "elements") * update_ffp('"', P '"') - * Cg(V "textpos", "endpos") + * Cg(V "anchor", "end_anchor") ), - longstring_literal = V "textpos" * Cs( + longstring_literal = V "anchor" * Cs( ((V "subordinate_indent" + V "empty_line") + C((V "unicode_escape" + (1 - (V "newline" + V "splice"))))) ^ 1 - ) * V "textpos" / create_literal, + ) * V "anchor" / create_literal, longstring = element( "string", P '""""' * V "indent" * Cg(Ct((V "longstring_literal" + V "splice") ^ 0), "elements") - * Cg(V "textpos", "endpos") + * Cg(V "anchor", "end_anchor") * V "dedent" ), comment_body = C((1 - V "newline") ^ 1), comment = update_ffp( "line comment", - element("comment", (P "#" * Cg(V "comment_body" ^ -1, "val") * Cg(V "textpos", "endpos"))) + element("comment", (P "#" * Cg(V "comment_body" ^ -1, "val") * Cg(V "anchor", "end_anchor"))) ), block_comment = update_ffp( "block comment", @@ -314,7 +343,7 @@ local grammar = P { P "####" * V "indent" * Cg(Cs((V "subordinate_indent" + V "comment_body" + V "empty_line") ^ 0), "val") - * Cg(V "textpos", "endpos") + * Cg(V "anchor", "end_anchor") * V "dedent" ) ) @@ -382,7 +411,7 @@ local grammar = P { table.insert(list.elements, 1, { kind = "symbol", str = list.elements["braceacc"], - anchor = list.anchor, + start_anchor = list.start_anchor, }) list.elements["braceacc"] = nil @@ -440,20 +469,20 @@ local grammar = P { acc = table.remove(argcalls, 1) table.insert(acc.elements, 1, symbol) - acc.anchor = symbol.anchor + acc.start_anchor = symbol.start_anchor if acc.elements["brace"] then table.insert(acc.elements, 1, acc.elements["brace"]) - acc.elements[1].anchor = acc.anchor + acc.elements[1].start_anchor = acc.start_anchor end for _, v in ipairs(argcalls) do table.insert(v.elements, 1, acc) - v.anchor = acc.anchor + v.start_anchor = acc.start_anchor acc = v if acc.elements["brace"] then table.insert(acc.elements, 1, acc.elements["brace"]) - acc.elements[1].anchor = acc.anchor + acc.elements[1].start_anchor = acc.start_anchor end end @@ -478,16 +507,16 @@ local grammar = P { symbol = symbol(V "symbol_chars"), } -local function span_error(position, subject, msg) +local function span_error(start_anchor, subject, msg) local lines = {} for line in subject:gmatch("([^\n\r]*)\r*\n") do table.insert(lines, line) end - local line = lines[position.line] or "" + local line = lines[start_anchor.line] or "" local _, tabnum = line:gsub("\t", "") - local caret_wsp = ("\t"):rep(tabnum) .. (" "):rep(position.char - (1 + tabnum)) - local linenum_wsp = (" "):rep(string.len(position.line)) + local caret_wsp = ("\t"):rep(tabnum) .. (" "):rep(start_anchor.char - (1 + tabnum)) + local linenum_wsp = (" "):rep(string.len(start_anchor.line)) local span = string.format( [[ @@ -498,11 +527,11 @@ error: %s %s |%s^ ]], msg, - position.sourceid, - position.line, - position.char, + start_anchor.sourceid, + start_anchor.line, + start_anchor.char, linenum_wsp, - position.line, + start_anchor.line, line, linenum_wsp, caret_wsp @@ -512,8 +541,8 @@ error: %s end ---@class FormatList ----@field anchor Anchor ----@field endpos Anchor +---@field start_anchor Anchor +---@field end_anchor Anchor ---@field kind LiteralKind ---@field elements table[] @@ -528,27 +557,24 @@ local function parse(input, filename) return nil end - local newlinetable = { + local line_ctx = { sourceid = filename, - positions = { { - pos = 1, - line = 1, - } }, + positions = { create_line_position(1, 1) }, } - local furthest_forward = { position = nil } - local ast = lpeg.match(grammar, input, 1, newlinetable, furthest_forward) + local furthest_forward_ctx = { start_anchor = nil } + local ast = lpeg.match(grammar, input, 1, line_ctx, furthest_forward_ctx) - if furthest_forward.position then + if furthest_forward_ctx.start_anchor then local expected = "{" - for i, v in ipairs(furthest_forward.expected) do + for i, v in ipairs(furthest_forward_ctx.expected) do expected = expected .. v .. ", " end expected = expected .. "}" - assert(false, span_error(furthest_forward.position, input, "expected " .. expected)) + assert(false, span_error(furthest_forward_ctx.start_anchor, input, "expected " .. expected)) end return ast end -return { parse = parse, anchor_mt = anchor_mt } +return { parse = parse, anchor_mt = anchor_mt, create_anchor = create_anchor } diff --git a/metalanguage.lua b/metalanguage.lua index d6452f85..c2c006d8 100644 --- a/metalanguage.lua +++ b/metalanguage.lua @@ -160,7 +160,7 @@ local reducer_mt = { __call = create_reducible } ---@class ExternalError ---@field cause any ----@field anchor Anchor +---@field start_anchor Anchor ---@field reducer_name string local ExternalError = {} @@ -169,7 +169,7 @@ local external_error_mt = { local message = "Lua error raised inside reducer " .. self.reducer_name .. " " - .. (self.anchor and tostring(self.anchor) or "at unknown position") + .. (self.start_anchor and tostring(self.start_anchor) or "at unknown position") .. ":\n" local cause = tostring(self.cause) if cause:find("table", 1, true) == 1 then @@ -188,12 +188,12 @@ local external_error_mt = { } ---@param cause any ----@param anchor Anchor +---@param start_anchor Anchor ---@param reducer_name string ---@return ExternalError -function ExternalError.new(cause, anchor, reducer_name) +function ExternalError.new(cause, start_anchor, reducer_name) return setmetatable({ - anchor = anchor, + start_anchor = start_anchor, cause = cause, reducer_name = reducer_name, }, external_error_mt) @@ -208,7 +208,7 @@ end ---@return ExternalError | any local function augment_error(syntax, reducer_name, ok, err_msg, ...) if not ok then - return false, ExternalError.new(err_msg, syntax.anchor, reducer_name) + return false, ExternalError.new(err_msg, syntax.start_anchor, reducer_name) end -- err_msg is the first result arg otherwise return err_msg, ... @@ -356,13 +356,13 @@ local symbol_exact = reducer(SymbolExact, "symbol exact") ---@class SyntaxError ---@field matchers Matcher[] ----@field anchor Anchor +---@field start_anchor Anchor ---@field cause any local SyntaxError = {} function SyntaxError:__tostring() local message = "Syntax error at anchor " - .. (self.anchor and tostring(self.anchor) or "") + .. (self.start_anchor and tostring(self.start_anchor) or "") .. " must be acceptable for one of:\n" local options = {} for k, v in ipairs(self.matchers) do @@ -385,13 +385,15 @@ local syntax_error_mt = { } ---@param matchers Matcher[] ----@param anchor Anchor +---@param start_anchor Anchor +---@param end_anchor Anchor ---@param cause any ---@return SyntaxError -local function syntax_error(matchers, anchor, cause) +local function syntax_error(matchers, start_anchor, end_anchor, cause) return setmetatable({ matchers = matchers, - anchor = anchor, + start_anchor = start_anchor, + end_anchor = end_anchor, cause = cause, }, syntax_error_mt) end @@ -404,7 +406,7 @@ end ---@class ConstructedSyntax ---@field accepters AccepterSet ----@field anchor Anchor +---@field start_anchor Anchor local ConstructedSyntax = {} --[[ @@ -447,7 +449,7 @@ function ConstructedSyntax:match(matchers, unmatched, extra) -- local name = getmetatable(matcher.reducible) -- print("rejected syntax kind", matcher.kind, name) end - return unmatched(extra, syntax_error(matchers, self.anchor, lasterr)) + return unmatched(extra, syntax_error(matchers, self.start_anchor, self.end_anchor, lasterr)) end local constructed_syntax_mt = { @@ -455,11 +457,15 @@ local constructed_syntax_mt = { } ---@param accepters AccepterSet ----@param anchor Anchor? +---@param start_anchor Anchor? +---@param end_anchor Anchor? ---@param ... any ---@return ConstructedSyntax -local function cons_syntax(accepters, anchor, ...) - return setmetatable({ accepters = accepters, anchor = anchor, ... }, constructed_syntax_mt) +local function cons_syntax(accepters, start_anchor, end_anchor, ...) + return setmetatable( + { accepters = accepters, start_anchor = start_anchor, end_anchor = end_anchor, ... }, + constructed_syntax_mt + ) end local pair_accepters = { @@ -468,12 +474,13 @@ local pair_accepters = { end, } ----@param anchor Anchor +---@param start_anchor Anchor +---@param end_anchor Anchor ---@param a ConstructedSyntax ---@param b ConstructedSyntax ---@return ConstructedSyntax -local function pair(anchor, a, b) - return cons_syntax(pair_accepters, anchor, a, b) +local function pair(start_anchor, end_anchor, a, b) + return cons_syntax(pair_accepters, start_anchor, end_anchor, a, b) end local symbol_accepters = { @@ -482,11 +489,12 @@ local symbol_accepters = { end, } ----@param anchor Anchor +---@param start_anchor Anchor +---@param end_anchor Anchor ---@param name string ---@return ConstructedSyntax -local function symbol(anchor, name) - return cons_syntax(symbol_accepters, anchor, name) +local function symbol(start_anchor, end_anchor, name) + return cons_syntax(symbol_accepters, start_anchor, end_anchor, name) end local value_accepters = { @@ -499,11 +507,12 @@ local value_accepters = { ---@field type string ---@field val any ----@param anchor Anchor +---@param start_anchor Anchor +---@param end_anchor Anchor ---@param val SyntaxValue ---@return ConstructedSyntax -local function value(anchor, val) - return cons_syntax(value_accepters, anchor, val) +local function value(start_anchor, end_anchor, val) + return cons_syntax(value_accepters, start_anchor, end_anchor, val) end local nil_accepters = { @@ -512,17 +521,24 @@ local nil_accepters = { end, } -local nilval = cons_syntax(nil_accepters) +---@param start_anchor Anchor +---@param end_anchor Anchor +---@return ConstructedSyntax +local function new_nilval(start_anchor, end_anchor) + return cons_syntax(nil_accepters, start_anchor, end_anchor) +end +local nilval = new_nilval() ----@param anchor Anchor +---@param start_anchor Anchor +---@param end_anchor Anchor ---@param a ConstructedSyntax ---@param ... ConstructedSyntax ---@return ConstructedSyntax -local function list(anchor, a, ...) +local function list(start_anchor, end_anchor, a, ...) if a == nil then - return nilval + return new_nilval(start_anchor, end_anchor) end - return pair(anchor, a, list(anchor, ...)) + return pair(start_anchor, end_anchor, a, list(start_anchor, end_anchor, ...)) end local any = reducer( @@ -760,6 +776,7 @@ local metalanguage = { list_tail_ends = list_tail_ends, reducer = reducer, isnil = isnil, + new_nilval = new_nilval, nilval = nilval, symbol_exact = symbol_exact, pair = pair, diff --git a/runtest.lua b/runtest.lua index f38b6fd9..fe76b2f6 100644 --- a/runtest.lua +++ b/runtest.lua @@ -122,57 +122,67 @@ if profile_run then print("Profile what:", profile_what) end -local filename = "testfile.alc" -local src_file, err = io.open(filename) -if not src_file then - error(err) -end -local src = src_file:read("a") +local prelude = "testfile.alc" -checkpointTime = os.clock() -print("Read code") -checkpointTime2 = checkpointTime -if print_src then - print(src) -end +local env = base_env.create() -print("Parsing code") -local code = format.read(src, filename) +local shadowed, env = env:enter_block(terms.block_purity.effectful) -checkpointTime = os.clock() -print(("Parsed! in %.3f seconds"):format(checkpointTime - checkpointTime2)) -checkpointTime2 = checkpointTime -if print_ast then - print("Printing raw AST") - print(format.lispy_print(code)) - print("End printing raw AST") -end +local function load_alc_file(name, env) + local src_file, err = io.open(name) + if not src_file then + error(err) + end + local src = src_file:read("a") -local env = base_env.create() + checkpointTime = os.clock() + print("Read code") + checkpointTime2 = checkpointTime + if print_src then + print(src) + end -local shadowed, env = env:enter_block(terms.block_purity.effectful) + print("Parsing code") + local code = format.read(src, name) -print("Expression -> terms") -if profile_run and profile_what == "match" then - profile.start() -end -local ok, expr, env = code:match( - { exprs.block(metalanguage.accept_handler, exprs.ExpressionArgs.new(terms.expression_goal.infer, env)) }, - metalanguage.failure_handler, - nil -) -if profile_run and profile_what == "match" then - profile.stop() - if profile_flame then - profile.dump_flame(profile_file) - else - profile.dump(profile_file) + checkpointTime = os.clock() + print(("Parsed! in %.3f seconds"):format(checkpointTime - checkpointTime2)) + checkpointTime2 = checkpointTime + if print_ast then + print("Printing raw AST") + print(format.lispy_print(code)) + print("End printing raw AST") end + + print("Expression -> terms") + if profile_run and profile_what == "match" then + profile.start() + end + local ok, expr, env = code:match({ + exprs.top_level_block( + metalanguage.accept_handler, + { exprargs = exprs.ExpressionArgs.new(terms.expression_goal.infer, env), name = name } + ), + }, metalanguage.failure_handler, nil) + if profile_run and profile_what == "match" then + profile.stop() + if profile_flame then + profile.dump_flame(profile_file) + else + profile.dump(profile_file) + end + end + if not ok then + checkpointTime = os.clock() + print(("Evaluating failed in %.3f seconds"):format(checkpointTime - checkpointTime2)) + print(expr) + return + end + return expr, env end -if not ok then - checkpointTime = os.clock() - print(("Evaluating failed in %.3f seconds"):format(checkpointTime - checkpointTime2)) - print(expr) + +local expr, env = load_alc_file(prelude, env) +if not expr or not env then return end @@ -218,7 +228,7 @@ evaluator.typechecker_state:flow( type, nil, terms.value.program_type( - terms.value.effect_row(set(unique_id)(terms.TCState), terms.value.effect_empty), + terms.value.effect_row(set(unique_id)(terms.TCState, terms.lua_prog), terms.value.effect_empty), evaluator.typechecker_state:metavariable(terms.typechecking_context()):as_value() ), nil, diff --git a/syntax-schema.lua b/syntax-schema.lua index 1f650c26..aa88af05 100644 --- a/syntax-schema.lua +++ b/syntax-schema.lua @@ -15,18 +15,18 @@ S:struct "anchor" "The source position information attached to a node" { local element = S:addstruct("element") element:define { - S.export.anchor "anchor"(0) "the source location of the element", + S.export.anchor "start_anchor"(0) "the source location of the element", schema.union "kind"(1) "What syntactic element this represents" { schema.variant "list"(2) { schema.list(element) "elements"(3), - S.export.anchor "endpos"(4), + S.export.anchor "end_anchor"(4), }, schema.variant "symbol"(5) { schema.text "str"(6), }, schema.variant "string"(7) { schema.list(element) "elements"(8) "A string contains a list of elements corresponding to parts of the literal. Every splice becomes a separate element, and the region between them is a literal byte buffer element", - S.export.anchor "endpos"(9), + S.export.anchor "end_anchor"(9), }, schema.variant "literal"(10) { schema.union "literaltype"(11) { diff --git a/terms.lua b/terms.lua index df6c5381..a75e2bad 100644 --- a/terms.lua +++ b/terms.lua @@ -145,9 +145,9 @@ end ---@param name string ---@param type value ---@param val value? ----@param anchor Anchor? +---@param start_anchor Anchor? ---@return TypecheckingContext -function TypecheckingContext:append(name, type, val, anchor) +function TypecheckingContext:append(name, type, val, start_anchor) if gen.builtin_string.value_check(name) ~= true then error("TypecheckingContext:append parameter 'name' must be a string") end @@ -164,18 +164,21 @@ function TypecheckingContext:append(name, type, val, anchor) error "BUG!!!" end if val ~= nil and value.value_check(val) ~= true then - error("TypecheckingContext:append parameter 'val' must be a value (or nil if given anchor)") + error("TypecheckingContext:append parameter 'val' must be a value (or nil if given start_anchor)") end - if anchor ~= nil and anchor_type.value_check(anchor) ~= true then - error("TypecheckingContext:append parameter 'anchor' must be an anchor (or nil if given val)") + if start_anchor ~= nil and anchor_type.value_check(start_anchor) ~= true then + error("TypecheckingContext:append parameter 'start_anchor' must be an start_anchor (or nil if given val)") end - if (val and anchor) or (not val and not anchor) then - error("TypecheckingContext:append expected either val or anchor") + if (val and start_anchor) or (not val and not start_anchor) then + error("TypecheckingContext:append expected either val or start_anchor") end local copy = { bindings = self.bindings:append({ name = name, type = type }), runtime_context = self.runtime_context:append( - val or value.neutral(neutral_value.free(free.placeholder(self:len() + 1, placeholder_debug(name, anchor)))) + val + or value.neutral( + neutral_value.free(free.placeholder(self:len() + 1, placeholder_debug(name, start_anchor))) + ) ), } return setmetatable(copy, typechecking_context_mt) @@ -256,13 +259,13 @@ binding:define_enum("binding", { { "annotated_lambda", { "param_name", gen.builtin_string, "param_annotation", inferrable_term, - "anchor", anchor_type, + "start_anchor", anchor_type, "visible", visibility, "pure", checkable_term, } }, { "program_sequence", { - "first", inferrable_term, - "anchor", anchor_type, + "first", inferrable_term, + "start_anchor", anchor_type, } }, }) @@ -290,7 +293,7 @@ inferrable_term:define_enum("inferrable", { "param_name", gen.builtin_string, "param_annotation", inferrable_term, "body", inferrable_term, - "anchor", anchor_type, + "start_anchor", anchor_type, "visible", visibility, "pure", checkable_term, } }, @@ -391,14 +394,14 @@ inferrable_term:define_enum("inferrable", { "alternate", inferrable_term, } }, { "host_intrinsic", { - "source", checkable_term, - "type", inferrable_term, --checkable_term, - "anchor", anchor_type, + "source", checkable_term, + "type", inferrable_term, --checkable_term, + "start_anchor", anchor_type, } }, { "program_sequence", { - "first", inferrable_term, - "anchor", anchor_type, - "continue", inferrable_term, + "first", inferrable_term, + "start_anchor", anchor_type, + "continue", inferrable_term, } }, { "program_end", { "result", inferrable_term } }, { "program_type", { @@ -639,8 +642,8 @@ typed_term:define_enum("typed", { "alternate", typed_term, } }, { "host_intrinsic", { - "source", typed_term, - "anchor", anchor_type, + "source", typed_term, + "start_anchor", anchor_type, } }, -- a list of upper and lower bounds, and a relation being bound with respect to @@ -699,8 +702,8 @@ typed_term:define_enum("typed", { -- stylua: ignore placeholder_debug:define_record("placeholder_debug", { - "name", gen.builtin_string, - "anchor", anchor_type, + "name", gen.builtin_string, + "start_anchor", anchor_type, }) -- stylua: ignore @@ -984,8 +987,8 @@ neutral_value:define_enum("neutral_value", { "alternate", value, } }, { "host_intrinsic_stuck", { - "source", neutral_value, - "anchor", anchor_type, + "source", neutral_value, + "start_anchor", anchor_type, } }, { "host_wrap_stuck", { "content", neutral_value } }, { "host_unwrap_stuck", { "container", neutral_value } }, diff --git a/test-format-adapter.lua b/test-format-adapter.lua index 7383c19e..91c6fc48 100644 --- a/test-format-adapter.lua +++ b/test-format-adapter.lua @@ -5,15 +5,15 @@ local function syntax_convert(tree) if tree.kind == "list" then local res = metalanguage.nilval for i = #tree.elements, 1, -1 do - res = metalanguage.pair(syntax_convert(tree.elements[i]), res) + res = metalanguage.pair(nil, nil, syntax_convert(tree.elements[i]), res) end return res elseif tree.kind == "symbol" then - return metalanguage.symbol(tree.str) + return metalanguage.symbol(nil, nil, tree.str) elseif tree.kind == "literal" then if tree.literaltype == "f64" then -- metalanguage.value use here is only correct for smoketest language and will need changed in future - return metalanguage.value(tree.val) + return metalanguage.value(nil, nil, tree.val) else error "syntax contains a literal of a type other than the basic number" end diff --git a/test.lua b/test.lua index 69878c9a..ba8c6a80 100644 --- a/test.lua +++ b/test.lua @@ -37,45 +37,25 @@ local function simplify_list(list) end end -local anchor_mt = { - __lt = function(fst, snd) - return snd.line > fst.line or (snd.line == fst.line and snd.char > fst.char) - end, - __le = function(fst, snd) - return fst < snd or fst == snd - end, - __eq = function(fst, snd) - return (snd.line == fst.line and snd.char == fst.char) - end, - - __tostring = function(self) - return "in file " .. self.sourceid .. ", line " .. self.line .. " character " .. self.char - end, -} - -local function create_anchor(line, char) - local anchor = { - char = char, - line = line, - sourceid = "inline", - } - - setmetatable(anchor, anchor_mt) - return anchor +local function create_anchor(line, char, sourceid) + if sourceid == nil then + sourceid = "inline" + end + return format.create_anchor(line, char, sourceid) end -local function create_list(anchor, endpos, elements) +local function create_list(start_anchor, end_anchor, elements) return { - anchor = anchor, - endpos = endpos, + start_anchor = start_anchor, + end_anchor = end_anchor, kind = "list", elements = elements, } end -local function create_symbol(anchor, symbol) +local function create_symbol(start_anchor, symbol) return { - anchor = anchor, + start_anchor = start_anchor, kind = "symbol", str = symbol, } @@ -87,10 +67,10 @@ local function forward_moving_cursor(element, cursor) if element.kind == "list" then if not cursor then - cursor = element.anchor + cursor = element.start_anchor else - if not (element.anchor >= cursor) then - -- print("failed, ", element.anchor, " >= ", cursor) + if not (element.start_anchor >= cursor) then + -- print("failed, ", element.start_anchor, " >= ", cursor) return false end end @@ -110,16 +90,16 @@ local function forward_moving_cursor(element, cursor) cursor = newcursor end - if not (element.endpos >= cursor) then - -- print("failed, ", element.endpos, " >= ", cursor) + if not (element.end_anchor >= cursor) then + -- print("failed, ", element.end_anchor, " >= ", cursor) return false end - cursor = element.endpos + cursor = element.end_anchor return true, cursor elseif element.kind == "literal" or element.kind == "symbol" or element.kind == "comment" then - -- print(element.anchor, " >= ", cursor, ", ", element.anchor >= cursor) - return element.anchor >= cursor, element.anchor + -- print(element.start_anchor, " >= ", cursor, ", ", element.start_anchor >= cursor) + return element.start_anchor >= cursor, element.start_anchor end return true, cursor @@ -128,23 +108,37 @@ end local function samelength_testfile_list(text, ast) local _, num_newlines = text:gsub("\n", "\n") - if not ((ast.endpos.line == num_newlines) or (ast.endpos.line == (num_newlines + 1))) then - print("ast: ", ast.endpos.line, "num_newlines:", num_newlines) + if not ((ast.end_anchor.line == num_newlines) or (ast.end_anchor.line == (num_newlines + 1))) then + print("ast: ", ast.end_anchor.line, "num_newlines:", num_newlines) end -- print(inspect(ast)) -- why is this even necessary?? - return (ast.endpos.line == num_newlines) or (ast.endpos.line == (num_newlines + 1)) + return (ast.end_anchor.line == num_newlines) or (ast.end_anchor.line == (num_newlines + 1)) end local function compare_list_anchors(actual, expected) if - (expected.anchor.line == actual.anchor.line and expected.anchor.char == actual.anchor.char) - and (expected.kind == actual.kind) + ( + expected.start_anchor.line == actual.start_anchor.line + and expected.start_anchor.char == actual.start_anchor.char + ) and (expected.kind == actual.kind) then if expected.kind == "list" then - if not (expected.endpos.line == actual.endpos.line and expected.endpos.char == actual.endpos.char) then - print("expected endpos: ", expected.endpos, expected.kind, " actual: ", actual.endpos, actual.kind) + if + not ( + expected.end_anchor.line == actual.end_anchor.line + and expected.end_anchor.char == actual.end_anchor.char + ) + then + print( + "expected end_anchor: ", + expected.end_anchor, + expected.kind, + " actual: ", + actual.end_anchor, + actual.kind + ) return false end @@ -156,7 +150,14 @@ local function compare_list_anchors(actual, expected) end return true else - print("expected anchor: ", expected.anchor, expected.kind, " actual: ", actual.anchor, actual.kind) + print( + "expected start_anchor: ", + expected.start_anchor, + expected.kind, + " actual: ", + actual.start_anchor, + actual.kind + ) return false end end diff --git a/testfile.alc b/testfile.alc index b3115548..33243333 100644 --- a/testfile.alc +++ b/testfile.alc @@ -27,9 +27,20 @@ let implicit-unwrap = lambda_implicit (T : type_(10, 0)) lambda (x : wrapped(T)) unwrap T x +let implicit-unstrict-wrap = lambda_curry ((T : type_(10, 0))) + lambda (x : T) + unstrict-wrap T x + +let implicit-unstrict-unwrap = lambda_implicit (T : type_(10, 0)) + lambda (x : unstrict-wrapped(T)) + unstrict-unwrap T x + let explicit-unwrap = unwrap let wrap = implicit-wrap let unwrap = implicit-unwrap +let explicit-unstrict-unwrap = unwrap +let unstrict-wrap = implicit-unstrict-wrap +let unstrict-unwrap = implicit-unstrict-unwrap let host-bool-wrap = intrinsic "return terms.value.host_bool_type" : wrapped(host-type) let host-string-wrap = intrinsic "return terms.value.host_string_type" : wrapped(host-type) @@ -412,6 +423,39 @@ let only-accept-host-tuples = host-unit wrapped void +let only-accept-prog-host-tuples-inner-host = + intrinsic + """" + local function check_prog_host_tuple(subject, consequent, alternate) + if not subject:is_program_type() then + return alternate + end + local effects, base = subject:unwrap_program_type() + if base:is_host_tuple_type() then + return consequent + else + return alternate + end + end + return check_prog_host_tuple + : + host-func-type (subject : wrapped(type), consequent : wrapped(host-type), alternate : wrapped(host-type)) -> ((result : wrapped(host-type))) + +let only-accept-prog-host-tuples-inner = + lambda (subject : wrapped(type), consequent : host-type, alternate : host-type) + let (res) = + only-accept-prog-host-tuples-inner-host + subject + wrap consequent + wrap alternate + unwrap res +let only-accept-prog-host-tuples = + lambda (subject : wrapped(type)) + only-accept-host-tuples-inner + subject + host-unit + wrapped void + let host-tuple-type-to-tuple-type-inner = intrinsic """" @@ -430,6 +474,26 @@ let host-tuple-type-to-tuple-type = let (res) = host-tuple-type-to-tuple-type-inner(t, valid) res +let extract-prog-host-tuple-type-inner = + intrinsic + """" + return function(prog_type) + local effect, base = prog_type:unwrap_program_type() + return base + end + : + host-func-type (t : wrapped(type), valid : only-accept-prog-host-tuples(t)) -> (res : wrapped(type), valid : only-accept-host-tuples(res)) + +let rebuild-prog-type = + intrinsic + """" + return function(prog_type, valid, new_base) + local effect, base = prog_type:unwrap_program_type() + return terms.value.program_type(effect, new_base) + end + : + host-func-type (t : wrapped(type), valid : only-accept-prog-host-tuples(t), b : wrapped(type)) -> ((res : wrapped(type))) + let host-tuple-to-tuple-inner = intrinsic """" @@ -498,7 +562,16 @@ let only-accept-host-funcs-inner-host = """" local function check_host_func(subject, consequent, alternate) if subject:is_host_function_type() then - return consequent + local param, result, info = subject:unwrap_host_function_type() + if not info:is_result_info() then + error "stuck result info? broken function type?" + end + local info_inner = info:unwrap_result_info() + if info_inner.purity:is_pure() then + return consequent + else + return alternate + end else return alternate end @@ -522,6 +595,44 @@ let only-accept-host-funcs = host-unit wrapped void +let only-accept-host-funcprogs-inner-host = + intrinsic + """" + local function check_host_func(subject, consequent, alternate) + if subject:is_host_function_type() then + local param, result, info = subject:unwrap_host_function_type() + if not info:is_result_info() then + error "stuck result info? broken function type?" + end + local info_inner = info:unwrap_result_info() + if info_inner.purity:is_effectful() then + return consequent + else + return alternate + end + else + return alternate + end + end + return check_host_func + : + host-func-type (subject : wrapped(type), consequent : wrapped(host-type), alternate : wrapped(host-type)) -> ((result : wrapped(host-type))) + +let only-accept-host-funcprogs-inner = + lambda (subject : wrapped(type), consequent : host-type, alternate : host-type) + let (res) = + only-accept-host-funcprogs-inner-host + subject + wrap consequent + wrap alternate + unwrap res +let only-accept-host-funcprogs = + lambda (subject : wrapped(type)) + only-accept-host-funcprogs-inner + subject + host-unit + wrapped void + let only-accept-funcs-inner-host = intrinsic """" @@ -562,6 +673,17 @@ let get-host-func-arg-inner = : host-func-type (subject : wrapped(type), valid : only-accept-host-funcs(subject)) -> (result : wrapped(type), valid : only-accept-host-tuples(result)) +let get-host-funcprog-arg-inner = + intrinsic + """" + local function get_host_func_arg(subject, valid) + local param_type, result_type, result_info = subject:unwrap_host_function_type() + return param_type, nil + end + return get_host_func_arg + : + host-func-type (subject : wrapped(type), valid : only-accept-host-funcprogs(subject)) -> (result : wrapped(type), valid : only-accept-host-tuples(result)) + let just-args = lambda (subject : wrapped(type), valid : only-accept-host-funcs(subject)) let (result, valid) = get-host-func-arg-inner(subject, valid) @@ -594,6 +716,9 @@ let set-func-result-info-inner = let func-conv-res-type = lambda (argtype : wrapped(type)) forall (arg : unwrap(argtype)) -> (res : wrapped(type), valid : only-accept-host-tuples(res)) +let funcprog-conv-res-type = + lambda (argtype : wrapped(type)) + forall (arg : unwrap(argtype)) -> (res : wrapped(type), valid : only-accept-prog-host-tuples(res)) let get-host-func-res-inner = intrinsic @@ -619,6 +744,29 @@ let get-host-func-res-inner = : host-func-type (subject : wrapped(type), valid : only-accept-host-funcs(subject)) -> ((results : wrapped(func-conv-res-type(just-args(subject, valid))))) +let get-host-funcprog-res-inner = + intrinsic + """" + local function get_host_func_res(subject, valid) + local param_type, result_type, result_info = subject:unwrap_host_function_type() + local typed_array = terms_gen.declare_array(terms.typed_term) + local tuple_build = terms.typed_term.tuple_cons( + typed_array( + terms.typed_term.host_wrap( + terms.typed_term.application( + terms.typed_term.bound_variable(1), + terms.typed_term.bound_variable(2) + ) + ), + terms.typed_term.literal(terms.value.host_value(nil)) + ) + ) + local ctx = terms.runtime_context():append(result_type) + return terms.value.closure("#TEST-1", tuple_build, ctx) + end + return get_host_func_res + : + host-func-type (subject : wrapped(type), valid : only-accept-host-funcprogs(subject)) -> ((results : wrapped(funcprog-conv-res-type(just-args(subject, valid))))) let foo = host-func-type (x : host-number, y : host-number) -> ((res : host-number)) @@ -672,11 +820,33 @@ let host-func-type-to-func-type = let (final-func-type-wrapped) = set-func-result-info-inner(wrap(new-func-type), host-nil, orig-result-info-wrapped) unwrap(final-func-type-wrapped) +let host-funcprog-type-to-funcprog-type = + lambda (T : type, valid : only-accept-host-funcprogs(wrap(T))) + let (oldargs oldargs-valid) = get-host-funcprog-arg-inner(wrap(T), valid) + let (newargs) = host-tuple-type-to-tuple-type-inner(oldargs, oldargs-valid) + let (orig-results-wrapped) = get-host-funcprog-res-inner(wrap(T), valid) + let orig-results = unwrap(orig-results-wrapped) + let (orig-result-info-wrapped) = get-host-func-result-info-inner(wrap(T), valid) + + let new-results = + lambda (args : unwrap(newargs)) + let ptuple = tuple-to-host-tuple(oldargs, oldargs-valid, args) + let (oldres oldres-valid) = apply(orig-results, ptuple) + let (extractres extractres-valid) = extract-prog-host-tuple-type-inner(oldres, oldres-valid) + let (newres) = host-tuple-type-to-tuple-type-inner(extractres, extractres-valid) + let (rebuilt) = rebuild-prog-type(oldres, oldres-valid, newres) + rebuilt + + let new-func-type = forall (x : unwrap(newargs)) -> (y : unwrap(new-results(x))) + let (final-func-type-wrapped) = set-func-result-info-inner(wrap(new-func-type), host-nil, orig-result-info-wrapped) + unwrap(final-func-type-wrapped) + host-func-type-to-func-type foo host-nil -let func-to-host-func = +let func-to-host-func-inner = intrinsic """" + local tunpack = unpack or table.unpack return function(_type, _valid, afn) return function(...) local args = table.pack(...) @@ -685,46 +855,88 @@ let func-to-host-func = conv_args:append(terms.value.host_value(args[i])) end local res = evaluator.apply_value(afn, terms.value.tuple_value(conv_args)) - if not res:is_host_value() then + if not res:is_tuple_value() then error "alicorn function converted to native function has failed to create a real value" end - return res:unwrap_host_value() + local elems = {} + for i, v in res:unwrap_tuple_value():ipairs() do + elems[i] = v:unwrap_host_value() + end + return tunpack(elems) end end : host-func-type (T : wrapped(host-type), valid : only-accept-host-funcs(T), fn : wrapped(host-func-type-to-func-type(unwrap(T), valid))) -> ((res-fn : unwrap(T))) +let func-to-host-func = + lambda (T : host-type, valid : only-accept-host-funcs(wrap(T)), fn : host-func-type-to-func-type(T, valid)) + let (res) = func-to-host-func-inner(wrap(T), valid, wrap(fn)) + res #TODO figure out why `unwrap(res) here doesn't make a nice type error but fails +let funcprog-to-host-funcprog-inner = + intrinsic + """" + local tunpack = unpack or table.unpack + return function(_type, _valid, afn) + return function(...) + local args = {...} + local nargs = select("#", ...) + local conv_args = terms_gen.declare_array(terms.value)() + for i = 1, nargs do + conv_args:append(terms.value.host_value(args[i])) + end + local res = evaluator.execute_program(evaluator.apply_value(afn, terms.value.tuple_value(conv_args))) + if not res:is_tuple_value() then + print(res) + error "alicorn function converted to native function has failed to create a real value" + end + local elems = {} + for i, v in res:unwrap_tuple_value():ipairs() do + elems[i] = v:unwrap_host_value() + end + return tunpack(elems) + end + end + : + host-func-type (T : wrapped(host-type), valid : only-accept-host-funcprogs(T), fn : wrapped(host-funcprog-type-to-funcprog-type(unwrap(T), valid))) -> ((res-fn : unwrap(T))) +let funcprog-to-host-funcprog = + lambda (T : host-type, valid : only-accept-host-funcprogs(wrap(T)), fn : host-funcprog-type-to-funcprog-type(T, valid)) + let (res) = funcprog-to-host-funcprog-inner(wrap(T), valid, wrap(fn)) + res #TODO figure out why `unwrap(res) here doesn't make a nice type error but fails +let host-if-type = + lambda (T : type_(9, 0)) + forall (subject : host-bool, consequent : T, alternate : T) -> (res : T) -let host-if-type = forall (subject : host-bool, consequent : host-type, alternate : host-type) -> (T : host-type) - -let host-if-wrap = intrinsic - """" - local typed = terms.typed_term - local string_array = terms_gen.declare_array(terms_gen.builtin_string) - return terms.value.closure( - "#host-if-param", - typed.tuple_elim( - string_array( - "#host-if-subject", - "#host-if-consequent", - "#host-if-alternate" - ), - typed.bound_variable(1), - 3, - typed.host_if( - typed.bound_variable(2), - typed.bound_variable(3), - typed.bound_variable(4) - ) - ), - terms.runtime_context() - ) - : - wrapped(host-if-type) -let host-if = unwrap(host-if-wrap) +let host-if = + lambda_implicit (T : type_(9, 0)) + let inner = + intrinsic + """" + local typed = terms.typed_term + local string_array = terms_gen.declare_array(terms_gen.builtin_string) + return terms.value.closure( + "#host-if-param", + typed.tuple_elim( + string_array( + "#host-if-subject", + "#host-if-consequent", + "#host-if-alternate" + ), + typed.bound_variable(1), + 3, + typed.host_if( + typed.bound_variable(2), + typed.bound_variable(3), + typed.bound_variable(4) + ) + ), + terms.runtime_context() + ) + : + wrapped(host-if-type(T)) + unwrap(inner) let tuple-desc-type-inner = intrinsic "return terms.value.tuple_desc_type" : host-func-type ((U : wrapped(universe))) -> ((T : wrapped(host-type))) @@ -951,6 +1163,68 @@ let host-tuple-concat = lambda ( let (cat) = inner(wrap(hd), wrap(tl)) unwrap(cat) +let host-number-fold-indep = lambda_implicit (T : type_(9, 0)) + let inner = + intrinsic + """" + local value_array = terms_gen.declare_array(terms.value) + return function(n, f, acc) + for i = n, 1, -1 do + acc = evaluator.apply_value(f, terms.value.tuple_value(value_array(terms.value.host_value(i), acc))) + end + return acc + end + : + host-func-type ( + n : host-number, + f : (wrapped (forall (i : host-number, acc : T) -> (resacc : T)) ), + acc : wrapped(T)) + -> + ((fold : wrapped(T))) + lambda (n : host-number, f : (forall (i : host-number, acc : T) -> (resacc : T)), acc : T) + let (fold) = inner(n, wrap(f), wrap(acc)) + unwrap(fold) + +let duplicate-tuple-desc = lambda_implicit (U : universe) + lambda (n : host-number, T : U) + host-number-fold-indep + n + lambda (i : host-number, acc : tuple-desc-type(U)) + tuple-desc-concat + U + tuple-desc-singleton U T + acc + tuple-desc-empty U + +let host-array-from-tuple = lambda_implicit (T : host-type) + lambda (size : host-number, tuple : host-tuple-type(duplicate-tuple-desc(size, T))) + let inner = + intrinsic + """" + return function(tuple) + return {tuple:unwrap_host_tuple_value(tuple):unpack()} + end + : + host-func-type ((tuple : wrapped(host-tuple-type(duplicate-tuple-desc(size, T))))) -> ((res : host-array-type(T))) + let (array) = inner(wrap(tuple)) + array + +let make-host-array = lambda_implicit (T : host-type) + lambda (size : host-number) + let input-type = host-tuple-type(duplicate-tuple-desc(size, T)) + let inner = + intrinsic + """" + return function(tuple) + return {tuple:unwrap_host_tuple_value(tuple):unpack()} + end + : + host-func-type ((tuple : wrapped(input-type))) -> ((res : host-array-type(T))) + lambda_single (elems : input-type) + let (array) = inner(wrap(elems)) + array + + let host-literal = new-host-type(new-host-unique-id("literal")) let host-expression-args = new-host-type(new-host-unique-id("expression-args")) @@ -1049,6 +1323,7 @@ let get-reducible-constructor = lambda ( let (c) = inner(red) c + let host-matcher-reducible = lambda ( userdata : host-type, storage : tuple-desc-type(host-type), @@ -1387,6 +1662,8 @@ let do-impl = lambda (syn : host-syntax, env : host-environment, ud : host-unit, 1 matcher + #let matchers = (make-host-array(1) matcher) + let (ok, term, inner_env) = match-syntax host-unit @@ -1770,75 +2047,88 @@ let terms-gen-map = new-host-type(new-host-unique-id("terms-gen-map")) let (res) = inner(f) res -#let HTTPServer = new-host-type(new-host-unique-id("HTTPServer")) -#let HTTPRequest = new-host-type(new-host-unique-id("HTTPRequest")) -#let HTTPResponse = new-host-type(new-host-unique-id("HTTPResponse")) -# -#let HTTPHandler = host-prog-type (req : HTTPRequest, res : HTTPResponse) -> () -# -#let string = host-string -# -#let create-http-server = -# lambda-prog ((callback : host-func-type-to-func-type(HTTPHandler, host-nil))) -# let inner = -# intrinsic -# """" -# local http = require "http" -# return http.createServer -# : -# host-prog-type ((handler : HTTPHandler)) -> ((res : HTTPServer)) -# let (host-cb) = func-to-host-func(wrap(HTTPHandler), host-nil, wrap(callback)) -# let (res) = inner(host-cb) -# res -# -#let http-server-listen = -# intrinsic -# "return function(server, host, port) server:listen(host, port) end" -# : -# host-prog-type (server : HTTPServer, host : string, port : host-number) -> () -# -#let response-set-header = -# intrinsic -# "return function(res, name, val) res:setHeader(name, val) end" -# : -# host-prog-type (res : HTTPResponse, name : string, value : string) -> () -# -#let response-send-body = -# intrinsic -# "return function(res, body) res:finish(body) end" -# : -# host-prog-type (res : HTTPResponse, body : string) -> () -# -#let strlen = -# do -# let inner = intrinsic "return function(str) return #str end" : (host-func-type ((str : string)) -> ((len : host-number))) -# lambda ((str : string)) -# let (res) = inner(str) -# res -# -#let num-to-str = -# do -# let inner = intrinsic "return tostring" : (host-func-type ((num : host-number)) -> ((str : string))) -# lambda ((num : host-number)) -# let (res) = inner(num) -# res -# -#let print = -# do -# let inner = intrinsic "return print" : (host-prog-type ((str : string)) -> ()) -# lambda-prog ((str : string)) -# let () = inner(str) -# -#let my-request-handler = lambda-prog (req : HTTPRequest, res : HTTPResponse) -# print "got a request!" -# let body = "Hello World!\n" -# response-set-header res "Content-Type" "text/plain" -# response-set-header res "Content-Length" num-to-str(strlen(body)) -# response-send-body res body -# -#let server = create-http-server(my-request-handler) -#http-server-listen server "0.0.0.0" 8080 -#print "server started on localhost:8080" +let HTTPServer = new-host-type(new-host-unique-id("HTTPServer")) +let HTTPRequest = new-host-type(new-host-unique-id("HTTPRequest")) +let HTTPResponse = new-host-type(new-host-unique-id("HTTPResponse")) + +let HTTPHandler = host-prog-type (req : HTTPRequest, res : HTTPResponse) -> () + +let string = host-string + +let create-http-server = + lambda-prog (callback : host-funcprog-type-to-funcprog-type(HTTPHandler, host-nil)) + let inner = + intrinsic + """" + local http = require "http" + local function createServer(handler) + local function wrapper(req, res) + evaluator.register_effect_handler(terms.lua_prog, evaluator.host_effect_handler) + return handler(req, res) + end + return http.createServer(wrapper) + end + return createServer + : + host-prog-type ((handler : HTTPHandler)) -> ((res : HTTPServer)) + let host-cb = funcprog-to-host-funcprog(HTTPHandler, host-nil, callback) + let (res) = inner(host-cb) + res + +let http-server-listen = + intrinsic + "return function(server, host, port) server:listen(port, host) end" + : + host-prog-type (server : HTTPServer, host : string, port : host-number) -> () + +let response-set-header = + intrinsic + "return function(res, name, val) res:setHeader(name, val) end" + : + host-prog-type (res : HTTPResponse, name : string, value : string) -> () + +let response-send-body = + intrinsic + "return function(res, body) res:finish(body) end" + : + host-prog-type (res : HTTPResponse, body : string) -> () + +let strlen = + do + let inner = intrinsic "return function(str) return #str end" : (host-func-type ((str : string)) -> ((len : host-number))) + lambda ((str : string)) + let (res) = inner(str) + res + +let num-to-str = + do + let inner = intrinsic "return tostring" : (host-func-type ((num : host-number)) -> ((str : string))) + lambda ((num : host-number)) + let (res) = inner(num) + res + +let print = + do + let inner = intrinsic "return print" : (host-prog-type ((str : string)) -> ()) + lambda-prog ((str : string)) + let () = inner(str) + +let runloop = + intrinsic "return require 'uv'.run" : (host-prog-type () -> ()) + +let my-request-handler = lambda-prog (req : HTTPRequest, res : HTTPResponse) + print "got a request!" + let body = "Hello World!\n" + response-set-header res "Content-Type" "text/plain" + response-set-header res "Content-Length" num-to-str(strlen(body)) + response-send-body res body + tuple-of-implicit() + +let main = lambda-prog () + let server = create-http-server(my-request-handler) + http-server-listen server "0.0.0.0" 8080 + print "server started on localhost:8080" + runloop() # let enum-desc-impl = lambda (syn : host-syntax, env : host-environment, ud : host-unit, goal : host-goal) # let matcher-t = host-matcher(host-unit, enum-desc-match-result-desc) @@ -1886,6 +2176,14 @@ let sqr_overcomplicated = lambda_annotated (t, x : t) : host-string x * x sqr_overcomplicated(host-number, 6) + + +#the tuple-type-explicit(host-type, duplicate-tuple-desc(1, host-number)) tuple-of-implicit(1) +#host-array-from-tuple(2, host-tuple-of(host-type, duplicate-tuple-desc(2, host-number))(1, 2)) + +#main() + + let switchtest = lambda (x) switch x none -> 2 diff --git a/testlanguage.lua b/testlanguage.lua index a8b090aa..df0f934d 100644 --- a/testlanguage.lua +++ b/testlanguage.lua @@ -99,7 +99,7 @@ local primitive_applicative_mt = { evalargs(metalanguage.accept_handler, env), }, metalanguage.failure_handler, nil) local res = self.fn(table.unpack(args)) - return true, metalanguage.value(res), env + return true, metalanguage.value(nil, nil, res), env end, }, } diff --git a/unformatter.lua b/unformatter.lua index 9d4acb1a..76d8202c 100644 --- a/unformatter.lua +++ b/unformatter.lua @@ -7,7 +7,7 @@ local indentation_char = "\t" local function unformat_list(ast, prev_line, indentation) local acc = "" - while ast.anchor.line > prev_line do + while ast.start_anchor.line > prev_line do acc = acc .. "\n" .. string.rep(indentation_char, indentation) prev_line = prev_line + 1 end @@ -33,7 +33,7 @@ local function unformat_list(ast, prev_line, indentation) acc = acc .. ast.str elseif ast.kind == "comment" then indentation = indentation + 1 - local multiline_comment = not (ast.anchor.line == ast.endpos.line) + local multiline_comment = not (ast.start_anchor.line == ast.end_anchor.line) if multiline_comment then acc = acc .. "####" @@ -51,7 +51,7 @@ local function unformat_list(ast, prev_line, indentation) acc = acc .. "\n" - prev_line = ast.endpos.line + prev_line = ast.end_anchor.line elseif ast.kind == "string" then indentation = indentation + 1 @@ -63,7 +63,7 @@ local function unformat_list(ast, prev_line, indentation) ["\t"] = [[\t]], } - local multiline_string = not (ast.anchor.line == ast.endpos.line) + local multiline_string = not (ast.start_anchor.line == ast.end_anchor.line) if multiline_string then acc = acc .. [[""""]] @@ -88,7 +88,7 @@ local function unformat_list(ast, prev_line, indentation) acc = acc .. [["]] end - prev_line = ast.endpos.line + prev_line = ast.end_anchor.line end return acc, prev_line @@ -98,7 +98,7 @@ local function unformat(ast) local acc = "" for _, v in ipairs(ast.elements) do - acc = acc .. "\n" .. unformat_list(v, v.anchor.line, 0) .. "" + acc = acc .. "\n" .. unformat_list(v, v.start_anchor.line, 0) .. "" end return acc