commit 126d3ce8810a0f15d5b0702602408c9a470c9a9c Author: Nathan Anderson Date: Sun Jul 13 16:05:27 2025 -0600 cyo file format has been created diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0f7a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.direnv/** +.zig-cache/** +zig-out/** diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c13bee --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# TODO +- cyo parser diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..cefc58f --- /dev/null +++ b/build.zig @@ -0,0 +1,91 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const exe_mod = b.createModule(.{ + // `root_source_file` is the Zig "entry point" of the module. If a module + // only contains e.g. external object files, you can make this `null`. + // In this case the main source file is merely a path, however, in more + // complicated build scripts, this could be a generated file. + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + // This creates another `std.Build.Step.Compile`, but this one builds an executable + // rather than a static library. + const exe = b.addExecutable(.{ + .name = "cyano", + .root_module = exe_mod, + }); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_module = exe_mod, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); + + // Create Check step for zls + const exe_check = b.addExecutable(.{ + .name = "cyano", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + const check = b.step("check", "Check if project compiles and tests pass, used by Zig Language Server"); + const check_unit_tests = b.addTest(.{ + .root_module = exe_mod, + }); + + const run_check_unit_tests = b.addRunArtifact(check_unit_tests); + + check.dependOn(&run_check_unit_tests.step); + check.dependOn(&exe_check.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..d918bb4 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,86 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .cyano, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // Together with name, this represents a globally unique package + // identifier. This field is generated by the Zig toolchain when the + // package is first created, and then *never changes*. This allows + // unambiguous detection of one package being an updated version of + // another. + // + // When forking a Zig project, this id should be regenerated (delete the + // field and run `zig build`) if the upstream project is still maintained. + // Otherwise, the fork is *hostile*, attempting to take control over the + // original project's identity. Thus it is recommended to leave the comment + // on the following line intact, so that it shows up in code reviews that + // modify the field. + .fingerprint = 0x61e18e6192e69e3f, // Changing this has security and trust implications. + + // Tracks the earliest Zig version that the package considers to be a + // supported use case. + .minimum_zig_version = "0.14.1", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. If the contents of a URL change this will result in a hash mismatch + // // which will prevent zig from using it. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + // + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/config/parser.cfg b/config/parser.cfg new file mode 100644 index 0000000..86e949f --- /dev/null +++ b/config/parser.cfg @@ -0,0 +1,11 @@ +look=[look,l] +examine=[examine,x] +examine==look + +take=[take,t] +grab=[grab,g] +get=[get] +take==grab==get + +put=[put] +place=[place,p] diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1f73ad9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1751984180, + "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..36e4625 --- /dev/null +++ b/flake.nix @@ -0,0 +1,23 @@ +{ + description = "Zig flake with ZLS"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + outputs = { + flake-utils, + nixpkgs, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + }; + in { + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + lldb + zig + zls + ]; + }; + }); +} diff --git a/src/cyo/content.zig b/src/cyo/content.zig new file mode 100644 index 0000000..5f6dda0 --- /dev/null +++ b/src/cyo/content.zig @@ -0,0 +1,3 @@ +const std = @import("std"); + +pub const CyoContent = struct {}; diff --git a/src/cyo/cyo.zig b/src/cyo/cyo.zig new file mode 100644 index 0000000..0945e20 --- /dev/null +++ b/src/cyo/cyo.zig @@ -0,0 +1 @@ +pub const content = @import("content.zig"); diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..999c2e1 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,27 @@ +const std = @import("std"); +const parser = @import("parser/parser.zig"); + +pub fn main() !void { + // parser.ActionParser.init(); +} + +test "test" { + _ = @import("parser/parser.zig"); +} +// test "simple test" { +// var list = std.ArrayList(i32).init(std.testing.allocator); +// defer list.deinit(); // Try commenting this out and see if zig detects the memory leak! +// try list.append(42); +// try std.testing.expectEqual(@as(i32, 42), list.pop()); +// } + +// test "fuzz example" { +// const Context = struct { +// fn testOne(context: @This(), input: []const u8) anyerror!void { +// _ = context; +// // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case! +// try std.testing.expect(!std.mem.eql(u8, "jaciXO", input)); +// } +// }; +// try std.testing.fuzz(Context{}, Context.testOne, .{}); +// } diff --git a/src/parser/action_config.zig b/src/parser/action_config.zig new file mode 100644 index 0000000..e68c609 --- /dev/null +++ b/src/parser/action_config.zig @@ -0,0 +1,58 @@ +const std = @import("std"); +const helpers = @import("parser_helpers.zig"); + +pub const ConfigError = error{ FileNotFound, OpenFileError }; + +pub const ActionConfig = struct { + allocator: std.mem.Allocator, + actions: std.ArrayList(std.StringHashMap([]const u8)), + ok: bool = false, + + pub fn init(allocator: std.mem.Allocator, reader: std.io.AnyReader) ActionConfig { + _ = reader; + // const actions = parseConfig(allocator, reader); + const actions = std.ArrayList(std.StringHashMap([]const u8)).init(allocator); + return .{ .allocator = allocator, .actions = actions, .ok = true }; + } + + pub fn deinit(this: *ActionConfig) void { + this.actions.deinit(); + } + + pub fn initFromFilePath(allocator: std.mem.Allocator, path: []const u8) ConfigError!ActionConfig { + const file = std.fs.cwd().openFile(path, .{.read_only}) catch |err| { + switch (err) { + std.fs.File.OpenError.FileNotFound => { + return ConfigError.FileNotFound; + }, + else => { + std.debug.print("Encountered unexpected openfile error: {a}", .{err}); + return ConfigError.OpenFileError; + }, + } + }; + defer file.close(); + + const actions = std.ArrayList(std.StringHashMap([]const u8)).init(allocator); + return .{ .allocator = allocator, .actions = actions, .ok = true }; + } + + // fn parseConfig(allocator: std.mem.Allocator, reader: std.io.AnyReader) !std.ArrayList(std.StringHashMap(std.ArrayList([]const u8))) { + // } +}; + +test "config ok" { + const allocator = std.testing.allocator; + const configReader = helpers.readerFromString( + \\ look=[L,Look] + ); + + const config = ActionConfig.init( + allocator, + configReader, + ); + + try std.testing.expect(config.ok); +} + +test "config bad" {} diff --git a/src/parser/cyo_parser.zig b/src/parser/cyo_parser.zig new file mode 100644 index 0000000..d3d5ab9 --- /dev/null +++ b/src/parser/cyo_parser.zig @@ -0,0 +1,21 @@ +const std = @import("std"); +const CyoContent = @import("../cyo/content.zig"); + +pub const CyoParser = struct { + allocator: std.mem.Allocator, + cyo_content: CyoContent, + + pub fn init(allocator: std.mem.Allocator, source_path: ?std.fs.path) CyoParser { + var cyo_content = try parseCyoFiles(allocator, source_path); + return .{ .allocator = allocator, .cyo_content = cyo_content }; + } + + fn parseCyoFiles(allocator: std.mem.Allocator, source_path: ?std.fs.path) CyoParser { + // 1. get all cyo files + // 2. process files + // 2a. lexical - validate file syntax + // 2b. syntactic parsing + // 2c. semantic - create objects and scenes + // 2d. evaluate - find missing or cyclical links + } +}; diff --git a/src/parser/input_parser.zig b/src/parser/input_parser.zig new file mode 100644 index 0000000..47e9d48 --- /dev/null +++ b/src/parser/input_parser.zig @@ -0,0 +1,77 @@ +const std = @import("std"); +const helpers = @import("parser_helpers.zig"); +const ActionConfig = @import("action_config.zig").ActionConfig; + +pub const InputParser = struct { + allocator: std.mem.Allocator, + config: ActionConfig, + content: ?[]const u8, + + pub fn init(allocator: std.mem.Allocator, configReader: std.io.AnyReader) !InputParser { + const config = ActionConfig.init(allocator, configReader); + return .{ .allocator = allocator, .config = config, .content = null }; + } + + pub fn deinit(self: *InputParser) void { + self.config.deinit(); + } + + pub fn parse(this: *InputParser, content: std.io.AnyReader) !ActionResult { + const lines = try content.readAllAlloc(this.allocator, 2048); + this.content = lines; + return ActionResult{}; + } +}; + +pub const ActionResult = struct { + // std.ArrayList() +}; + +test "init no-op" { + const allocator = std.testing.allocator; + const reader = helpers.readerFromString(""); + + var parser = try InputParser.init(allocator, reader); + _ = parser.parse(helpers.readerFromString("")) catch { + try std.testing.expect(false); + }; +} + +// test "basic command" { +// const allocator = std.testing.allocator_instance; + +// var parser = ActionParser.init( +// allocator, +// ); +// var result = try parser.parse("Look"); +// std.testing.expectEqual( +// result, +// .ParseResult{}, +// ); +// } + +// test "noun" { +// const allocator = std.testing.allocator_instance; + +// var parser = ActionParser.init( +// allocator, +// ); +// var result = try parser.parse("Backpack"); +// std.testing.expectEqual( +// result, +// .ParseResult{}, +// ); +// } + +// test "basic suggestion" { +// const allocator = std.testing.allocator_instance; + +// var parser = ActionParser.init( +// allocator, +// ); +// var result = try parser.parse("Look at ky"); +// std.testing.expectEqual( +// SubjectSuggestion("key"), +// result.suggestions[0], +// ); +// } diff --git a/src/parser/parser.zig b/src/parser/parser.zig new file mode 100644 index 0000000..f7ec624 --- /dev/null +++ b/src/parser/parser.zig @@ -0,0 +1,2 @@ +pub const input = @import("input_parser.zig"); +pub const cyo = @import("cyo_parser.zig"); diff --git a/src/parser/parser_helpers.zig b/src/parser/parser_helpers.zig new file mode 100644 index 0000000..246ad58 --- /dev/null +++ b/src/parser/parser_helpers.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +pub fn readerFromString(input: []const u8) std.io.AnyReader { + var reader = std.io.fixedBufferStream(input); + return reader.reader().any(); +} + +pub fn toLower(alloc: std.mem.Allocator, input: []const u8) ![]const u8 { + var lowered = try alloc.alloc(u8, input.len); + for (0..input.len) |i| { + lowered[i] = std.ascii.toLower(input[i]); + } + return lowered; +} + +test "toLower" { + const lowered = try toLower(std.testing.allocator, "LOWER"); + defer std.testing.allocator.free(lowered); + try std.testing.expectEqualStrings("lower", lowered); + + const lowered2 = try toLower(std.testing.allocator, "LOWER123"); + defer std.testing.allocator.free(lowered2); + try std.testing.expectEqualStrings("lower123", lowered2); + + const lowered3 = try toLower(std.testing.allocator, "LoWeR"); + defer std.testing.allocator.free(lowered3); + try std.testing.expectEqualStrings("lower", lowered3); + + const lowered4 = try toLower(std.testing.allocator, "__LOWER!%*(#!@)"); + defer std.testing.allocator.free(lowered4); + try std.testing.expectEqualStrings("__lower!%*(#!@)", lowered4); +} diff --git a/srv/items.cy b/srv/items.cy new file mode 100644 index 0000000..1dec750 --- /dev/null +++ b/srv/items.cy @@ -0,0 +1,4 @@ +[Duct Tape] +d:The tape that does it all. + +[Flash Light] diff --git a/srv/main.cy b/srv/main.cy new file mode 100644 index 0000000..9e95962 --- /dev/null +++ b/srv/main.cy @@ -0,0 +1,17 @@ +[Warehouse] + +Your vision blurs as you fumble for the tablet. The glow of your fingers is nearly gone. The salty tablet is the last +sensation before everything goes dark. + +--- + +You awaken. How long has it been? Thanks to your dull glow, you can read the clock on the wall. 8:36. Only a few hours. +Thank God you brought one with you. But its not a cure, just a band-aid. Time is ticking. You've got 24 hours +to breathe, better get back before then. + +[Warehouse.d] +It might have been an Amazon warehouse. Everything seems sacked. Empty shelves twist in some corporate labrynth before you. +Would have been nice to have something to show for this trip. But information isn't useless. + +[Warehouse.i] +Duct Tape