--- /dev/null
+# Package
+
+version = "0.1.0"
+author = "Brendan Hansen"
+description = "A new awesome nimble package"
+license = "MIT"
+srcDir = "src"
+bin = @["christmas_proj"]
+
+
+# Dependencies
+
+requires "nim >= 0.19.0"
+requires "opengl >= 1.2.0"
+requires "glfw"
+
+task run, "Run project":
+ exec("nimble build")
+ exec("./christmas_proj")
\ No newline at end of file
--- /dev/null
+$MEM_1 = 0
+$MEM_2 = 1
+
+WHILE $MEM_2 < 100000 DO
+ $MEM_3 = $MEM_1 + $MEM_2
+ $MEM_1 = $MEM_2
+ $MEM_2 = $MEM_3
+END
\ No newline at end of file
--- /dev/null
+#version 300 es
+
+precision mediump float;
+
+in vec4 v_col;
+
+out vec4 fragColor;
+
+void main() {
+ fragColor = v_col * vec4(1.0, 1.0, 1.0, 1.0);
+}
\ No newline at end of file
--- /dev/null
+#version 300 es
+
+//uniform mat3 u_proj;
+layout(location = 0) in vec2 a_pos;
+layout(location = 1) in vec4 a_col;
+
+out vec4 v_col;
+
+void main() {
+ float scalar = float(gl_InstanceID) * 2.f - 1.f;
+ gl_Position = vec4(a_pos, 0.0, 0.0) * scalar + vec4(0, 0, 0, 1);
+ v_col = a_col;
+}
\ No newline at end of file
--- /dev/null
+ROBOT PROGRAMMING AND VISUALS
+
+Create custom programming language that instructions robots to move around a grid and perform tasks.
+Visualize this system with OpenGL 3.0 ES
+
+Language may look something like this:
+
+$A = 10
+:LOOP_TOP
+ IF $A ~= 0 THEN
+ $A = $A - 1
+ GOTO :LOOP_TOP
+ END
+
+This is compiled to a bytecode, which is executed by the program.
+Language is compiled at runtime, then bytecode is used.
\ No newline at end of file
--- /dev/null
+import os
+import sequtils
+import opengl
+import glfw3 as glfw
+import gfx/window as gfx_window
+import gfx/glutils as glutils
+
+import lang/ast
+import lang/lexer as lexer
+import lang/parser as parser
+import lang/tokens
+
+proc key_down(window: glfw.Window, key, scancode, action, modifier: cint) {.cdecl.} =
+ if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
+ glfw.SetWindowShouldClose(window, glfw.TRUE)
+
+proc main2() =
+ let window = gfxWindow.NewWindow(800, 600, "ASDF")
+
+ window.SetKeyCallback(key_down)
+
+ var version = cast[cstring](glGetString(GL_VERSION))
+ echo $version
+
+ let vertex_shader = glutils.CreateShader(glutils.stVertex, "./data/shaders/basic.vert")
+ let fragment_shader = glutils.CreateShader(glutils.stFragment, "./data/shaders/basic.frag")
+ let program = glutils.LinkProgram(vertex_shader, fragment_shader)
+ glUseProgram(program)
+
+ var vao: GLuint
+ var temp_vao: GLuint
+ glGenVertexArrays(1.GLsizei, vao.addr)
+ glGenVertexArrays(1.GLsizei, temp_vao.addr)
+
+ var vbo: GLuint
+ var cbo: GLuint
+ var ibo: GLuint
+ glGenBuffers(1.GLsizei, vbo.addr)
+ glGenBuffers(1.GLsizei, cbo.addr)
+ glGenBuffers(1.GLsizei, ibo.addr)
+
+ glBindVertexArray(vao)
+ glEnableVertexAttribArray(0)
+ glEnableVertexAttribArray(1)
+
+ var vertex_data: array[6, GLfloat] = [
+ 0'f32, 0'f32,
+ 0'f32, 1'f32,
+ 1'f32, 0'f32,
+ ]
+ glBindBuffer(GL_ARRAY_BUFFER, vbo)
+ glBufferData(GL_ARRAY_BUFFER, vertex_data.sizeof, vertex_data.addr, GL_STATIC_DRAW)
+ glVertexAttribPointer(0, 2, cGL_FLOAT, GL_FALSE, 8.GLsizei, nil)
+
+ var color_data: array[8, GLfloat] = [
+ 1'f32, 0'f32, 1'f32, 1'f32,
+ 0'f32, 1'f32, 0'f32, 1'f32,
+ ]
+ glBindBuffer(GL_ARRAY_BUFFER, cbo)
+ glBufferData(GL_ARRAY_BUFFER, color_data.sizeof, color_data.addr, GL_STATIC_DRAW)
+ glVertexAttribDivisor(1, 1)
+ glVertexAttribPointer(1, 4, cGL_FLOAT, GL_FALSE, 16.GLsizei, nil)
+
+ var index_data: array[3, GLuint] = [
+ 0.GLuint, 1.Gluint, 2.Gluint,
+ ]
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_data.sizeof, index_data.addr, GL_STATIC_DRAW)
+
+ glBindVertexArray(0)
+
+ while not window.ShouldClose():
+ glClearColor(0, 0, 0, 1)
+ glClear(GL_COLOR_BUFFER_BIT)
+
+ glBindVertexArray(vao)
+ glDrawElementsInstanced(GL_TRIANGLES, 3.GLsizei, GL_UNSIGNED_INT, nil, 2)
+ glBindVertexArray(0)
+
+ window.Refresh()
+
+ window.CloseWindow()
+
+proc main() =
+ let source_code = readFile("data/progs/test.lgt")
+ let tokens = toSeq(lexer.GenerateTokens(source_code))
+ for token in tokens:
+ echo token
+
+ echo "\n\n\n"
+
+ for exp in parser.ParseTokens(tokens):
+ echo exp
+
+when isMainModule:
+ main()
--- /dev/null
+import os
+import opengl
+
+type
+ ShaderType* = enum
+ stVertex, stFragment
+
+proc Compile_shader*(shaderType: ShaderType, source: cstringArray): GLuint =
+ let s_type =
+ case shaderType
+ of stVertex:
+ GL_VERTEX_SHADER
+ of stFragment:
+ GL_FRAGMENT_SHADER
+
+ let shader = glCreateShader s_type
+ glShaderSource shader, 1, source, nil
+ glCompileShader shader
+
+ var status: GLint
+ glGetShaderiv shader, GL_COMPILE_STATUS, status.addr
+ if status == 0:
+ var logSize: GLint
+ glGetShaderiv shader, GL_INFO_LOG_LENGTH, logSize.addr
+
+ var
+ errorMsg = cast[ptr GLchar](alloc logSize)
+ logLen: GLsizei
+
+ glGetShaderInfoLog shader, logSize.GLsizei, logLen.addr, errorMsg
+
+ echo "Error compiling shader: " & $errorMsg
+
+ dealloc errorMsg
+ return 0
+
+ return shader
+
+proc Create_shader*(shaderType: ShaderType, fileLocation: string): GLuint =
+ let source = readFile fileLocation
+ let c_string = allocCStringArray([source])
+
+ # Dealloc the string when the procedure returns
+ defer: deallocCStringArray(c_string)
+
+ return Compile_shader(shaderType, c_string)
+
+proc Link_program*(vertex_shader, fragment_shader: GLuint): GLuint =
+ let program = glCreateProgram()
+ glAttachShader program, vertex_shader
+ glAttachShader program, fragment_shader
+ glLinkProgram program
+
+ var status: GLint
+ glGetProgramiv program, GL_LINK_STATUS, status.addr
+ if status == 0:
+ var logSize: GLint
+ glGetProgramiv program, GL_INFO_LOG_LENGTH, logSize.addr
+
+ var
+ errorMsg = cast[ptr GLchar](alloc logSize)
+ logLen: GLsizei
+
+ glGetProgramInfoLog program, logSize.GLsizei, logLen.addr, errorMsg
+
+ echo "Error linking program: " & $errorMsg
+
+ dealloc errorMsg
+ return 0
+
+ return program
--- /dev/null
+import glfw3 as glfw
+import opengl
+
+type
+ Window* = object
+ window: glfw.Window
+ width, height: cint
+ title: cstring
+
+
+proc New_window*(width, height: cint, title: cstring): Window =
+ # Initialize GLFW
+ if glfw.Init() != glfw.TRUE:
+ echo "Failed to initialize GLFW"
+ return
+
+ # Setup error callback for OpenGL
+ # May use logging system in the future
+ discard glfw.SetErrorCallback do (errorCode: cint, description: cstring) {.cdecl.}:
+ echo "GLFW Error: " & $description
+
+ # Create the window
+ let window = glfw.CreateWindow(width, height, title, nil, nil)
+
+ # Check for failed window creation
+ if window == nil:
+ echo "Window or context creation failed"
+
+ # Use the window for OpenGL operations
+ glfw.MakeContextCurrent(window)
+ glfw.SwapInterval(0)
+
+ # Initialize OpenGL
+ opengl.loadExtensions()
+
+ # Create the wrapper around our window
+ let m_window = Window(window: window, width: width, height: height, title: title)
+
+ return m_window
+
+func Get_size*(window: Window): (int, int) {.inline.} =
+ (int(window.width), int(window.height))
+
+proc Set_key_callback*(window: Window, callback: glfw.KeyFun) {.inline.} =
+ discard glfw.SetKeyCallback(window.window, callback)
+
+proc Should_close*(window: Window): bool {.inline.} =
+ glfw.WindowShouldClose(window.window) == glfw.TRUE
+
+proc Refresh*(window: Window) =
+ glfw.SwapBuffers(window.window)
+ glfw.PollEvents()
+
+proc Close_window*(window: Window) =
+ glfw.DestroyWindow(window.window)
+ glfw.Terminate()
\ No newline at end of file
--- /dev/null
+import ./types
+import ./tokens
+
+type
+ LightExprType* = enum
+ leNull = 0,
+ leVar = 1,
+ leNumLit = 2,
+ leOp = 3,
+ leAssign = 4,
+ leLabel = 5,
+ leGoto = 6,
+ leIf = 7,
+ leWhile = 8,
+ leBreak = 9
+
+ LightExpr* = ref object
+ case kind*: LightExprType
+ of leVar:
+ var_name*: LightVariable
+ of leNumLit:
+ value*: LightInt
+ of leOp:
+ left*: LightExpr
+ right*: LightExpr
+ operation*: LightOperation
+ of leAssign:
+ variable*: LightVariable
+ expression*: LightExpr
+ of leLabel, leGoto:
+ label*: string
+ of leIf, leWhile:
+ condition*: LightExpr
+ body*: seq[LightExpr]
+ else:
+ discard
+
+proc `$`*(exp: LightExpr): string =
+ case exp.kind:
+ of leVar: "Var[" & $exp.var_name & "]"
+ of leNumLit: "Num[" & $exp.value & "]"
+ of leOp: "Operation[" & $exp.operation & ", " & $exp.left & ", " & $exp.right & "]"
+ of leAssign: "Assignment[" & $exp.variable & ", " & $exp.expression & "]"
+ of leLabel: "Label[" & $exp.label & "]"
+ of leGoto: "Goto[" & $exp.label & "]"
+ of leIf: "If[" & $exp.condition & " -> " & $exp.body & "]"
+ of leWhile: "While[" & $exp.condition & " -> " & $exp.body & "]"
+ of leBreak: "Break"
+ else: ""
\ No newline at end of file
--- /dev/null
+import strutils
+import parseutils
+import sequtils
+
+import ./types
+import ./tokens
+
+iterator Generate_tokens*(source: string): LightToken =
+ for token, is_sep in strutils.tokenize(source, {' ', '\n', ';', '\t', '='}):
+ if is_sep:
+ if token.contains({'\n', ';'}):
+ yield LightToken(kind: ltExprDelim)
+ if token.contains({'='}):
+ yield LightToken(kind: ltEq)
+ continue
+
+ if token.startsWith('$'):
+ let varString = token[1 .. ^1]
+ var varName: LightVariable
+
+ if varString == "":
+ raise newException(IOError, "Expected variable name")
+ else:
+ if varString == "MEM_1": varName = var1
+ elif varString == "MEM_2": varName = var2
+ elif varString == "MEM_3": varName = var3
+ elif varString == "MEM_4": varName = var4
+ elif varString == "MEM_5": varName = var5
+ elif varString == "MEM_6": varName = var6
+ elif varString == "MEM_7": varName = var7
+ elif varString == "MEM_8": varName = var8
+ else:
+ raise newException(IOError, "Invalid variable name.")
+
+ yield LightToken(kind: ltVar, var_name: varName)
+
+ elif token == "=":
+ yield LightToken(kind: ltEq)
+
+ elif token.isDigit:
+ var value: int
+ discard parseutils.parseInt(token, value)
+
+ yield LightToken(kind: ltNum, value: value.LightInt)
+
+ elif token.toLowerAscii == "if":
+ yield LightToken(kind: ltIf)
+ elif token.toLowerAscii == "while":
+ yield LightToken(kind: ltWhile)
+ elif token.toLowerAscii == "break":
+ yield LightToken(kind: ltBreak)
+ elif token.toLowerAscii == "then" or token.toLowerAscii == "do" or token == "{":
+ yield LightToken(kind: ltBlockStart)
+ elif token.toLowerAscii == "end" or token == "}":
+ yield LightToken(kind: ltBlockEnd)
+
+ elif token.toLowerAscii == "goto":
+ yield LightToken(kind: ltGoto)
+ elif token.startsWith(':'):
+ let labelName = token[1 .. ^1]
+ yield LightToken(kind: ltLabel, label_name: labelName)
+
+ else:
+ case token:
+ of "+":
+ yield LightToken(kind: ltOp, operation: loAdd)
+ of "-":
+ yield LightToken(kind: ltOp, operation: loSub)
+ of "*":
+ yield LightToken(kind: ltOp, operation: loMul)
+ of "/":
+ yield LightToken(kind: ltOp, operation: loDiv)
+ of "%":
+ yield LightToken(kind: ltOp, operation: loMod)
+ of "<":
+ yield LightToken(kind: ltOp, operation: loLt)
+ of "<=":
+ yield LightToken(kind: ltOp, operation: loLte)
+ of ">":
+ yield LightToken(kind: ltOp, operation: loGt)
+ of ">=":
+ yield LightToken(kind: ltOp, operation: loGte)
+ of "==":
+ yield LightToken(kind: ltOp, operation: loEq)
+ of "~=":
+ yield LightToken(kind: ltOp, operation: loNeq)
+ else:
+ raise newException(IOError, "Invalid token")
--- /dev/null
+
+import ./types
+import ./tokens
+import ./ast
+
+import ../utils/iter
+
+type
+ LightParser* = object
+ tokens: Iter[LightToken]
+
+func CreateParser*(tokens: Iter[LightToken]): LightParser =
+ LightParser(tokens: tokens)
+func CreateParser*(tokens: seq[LightToken]): LightParser =
+ CreateParser(CreateIter[LightToken](tokens, LightToken(kind: ltNull)))
+
+func NextExpr*(parser: LightParser, prev: LightExpr, stop_at: set[LightTokenType]): LightExpr
+
+func Parse_block(tokens: Iter[LightToken]): seq[LightExpr] =
+ result = @[]
+
+ var last = tokens.Current
+ if last.kind == ltBlockEnd:
+ tokens.Step()
+ return
+
+ var parser = CreateParser(tokens)
+ while last.kind != ltBlockEnd:
+ let p = parser.NextExpr(LightExpr(kind: leNull), {ltExprDelim, ltBlockEnd})
+ if p.kind != leNull:
+ result.add(p)
+ last = parser.tokens.Current
+ parser.tokens.Step()
+
+func NextExpr*(parser: LightParser, prev: LightExpr, stop_at: set[LightTokenType]): LightExpr =
+ let curr = parser.tokens.Current
+
+ if curr.kind in stop_at:
+ return prev
+
+ parser.tokens.Step()
+
+ if curr.kind in {ltNum, ltVar} and prev.kind == leNull:
+ let prevExpr =
+ case curr.kind:
+ of ltNum: LightExpr(kind: leNumLit, value: curr.value)
+ of ltVar: LightExpr(kind: leVar, var_name: curr.var_name)
+ else: LightExpr(kind: leNull)
+
+ return parser.NextExpr(prevExpr, stop_at)
+
+ elif curr.kind == ltOp:
+ let next = parser.NextExpr(LightExpr(kind: leNull), stop_at)
+ return LightExpr(
+ kind: leOp,
+ left: prev,
+ right: next,
+ operation: curr.operation
+ )
+
+ elif curr.kind == ltEq:
+ if prev.kind != leVar:
+ raise newException(ValueError, "Expected variable on the left of assignment operator")
+
+ let next = parser.NextExpr(LightExpr(kind: leNull), stop_at)
+
+ return LightExpr(
+ kind: leAssign,
+ variable: prev.var_name,
+ expression: next
+ )
+
+ elif curr.kind == ltLabel:
+ return LightExpr(
+ kind: leLabel,
+ label: curr.label_name
+ )
+
+ elif curr.kind == ltGoto:
+ let next = parser.tokens.Current
+ if next.kind != ltLabel:
+ raise newException(ValueError, "Expected label after goto")
+
+ return LightExpr(
+ kind: leGoto,
+ label: next.label_name
+ )
+
+ elif curr.kind == ltIf:
+ let condition = parser.NextExpr(LightExpr(kind: leNull), {ltBlockStart})
+ parser.tokens.Step()
+ let body = Parse_block(parser.tokens)
+
+ return LightExpr(
+ kind: leIf,
+ condition: condition,
+ body: body
+ )
+
+ elif curr.kind == ltWhile:
+ let condition = parser.NextExpr(LightExpr(kind: leNull), {ltBlockStart})
+ parser.tokens.Step()
+ let body = Parse_block(parser.tokens)
+
+ return LightExpr(
+ kind: leWhile,
+ condition: condition,
+ body: body
+ )
+
+ elif curr.kind == ltBreak:
+ return LightExpr(
+ kind: leBreak
+ )
+
+iterator Parse_tokens*(tokens: seq[LightToken]): LightExpr =
+ var parser = CreateParser(tokens)
+ while not parser.tokens.ReachedEnd:
+ let next = parser.NextExpr(LightExpr(kind: leNull), {ltExprDelim})
+ if next.kind != leNull:
+ yield next
+ parser.tokens.Step()
--- /dev/null
+import ./types
+
+type
+ LightTokenType* = enum
+ ltNull,
+ ltVar, ltNum,
+ ltExprDelim,
+ ltLabel, ltGoto,
+ ltIf, ltWhile, ltBlockStart, ltBlockEnd,
+ ltBreak,
+ ltOp, ltEq
+
+ LightToken* = ref object
+ case kind*: LightTokenType
+ of ltVar:
+ var_name*: LightVariable
+ of ltNum:
+ value*: LightInt
+ of ltLabel:
+ label_name*: string
+ of ltOp:
+ operation*: LightOperation
+ else:
+ discard
+
+func `$`*(variable: LightVariable): string =
+ case variable:
+ of var1: "MEM_1"
+ of var2: "MEM_2"
+ of var3: "MEM_3"
+ of var4: "MEM_4"
+ of var5: "MEM_5"
+ of var6: "MEM_6"
+ of var7: "MEM_7"
+ of var8: "MEM_8"
+
+proc `$`*(token: LightToken): string =
+ return
+ case token.kind:
+ of ltNull: "NullToken"
+ of ltVar: "VarToken[" & $token.var_name & "]"
+ of ltEq: "EqualsToken"
+ of ltNum: "NumberToken[" & $token.value & "]"
+ of ltExprDelim: "ExprDelimToken"
+ of ltLabel: "LabelToken[" & token.label_name & "]"
+ of ltGoto: "GotoToken"
+ of ltIf: "IfToken"
+ of ltWhile: "WhileToken"
+ of ltBreak: "BreakToken"
+ of ltBlockStart: "BlockStartToken"
+ of ltBlockEnd: "BlockEndToken"
+ of ltOp: "OpeartionToken[" & $token.operation & "]"
\ No newline at end of file
--- /dev/null
+type
+ LightVariable* = enum
+ var1 = 0, var2 = 1, var3 = 2, var4 = 3,
+ var5 = 4, var6 = 5, var7 = 6, var8 = 7
+
+ LightOperation* = enum
+ loAdd, loSub, loMul, loDiv, loMod,
+ loGt, loGte, loLt, loLte, loEq, loNeq
+
+ LightInt* = int32
--- /dev/null
+type
+ Iter*[T] = ref object
+ curr: int
+ list: seq[T]
+ length: int
+ empty: T
+
+proc CreateIter*[T](items: seq[T], empty: T): Iter[T] =
+ Iter[T](curr: 0, list: items, length: items.len, empty: empty)
+
+proc Step*[T](iter: Iter[T]) =
+ iter.curr += 1
+
+func ReachedEnd*[T](iter: Iter[T]): bool =
+ iter.curr >= iter.length
+
+proc Current*[T](iter: Iter[T]): T =
+ if iter.curr < 0 or iter.curr >= iter.length:
+ return iter.empty
+
+ return iter.list[iter.curr]
+
+proc Previous*[T](iter: Iter[T]): T =
+ if iter.curr - 1 < 0 or iter.curr - 1 >= iter.length:
+ return iter.empty
+
+ return iter.list[iter.curr - 1]
+
+proc Next*[T](iter: Iter[T]): T =
+ if iter.curr + 1 < 0 or iter.curr + 1 >= iter.length:
+ return iter.empty
+
+ return iter.list[iter.curr + 1]
+